2019.08.09【NOIP提高组】模拟 A 组 比赛过程及题解

退役选手为了维持一下手感,从必刷题和金考卷的海洋的跳出来刷点题

感谢symbol一套题把我的水准由普及给拉上来了

比赛过程

早上八点挣扎着从床上起来看题

感觉T1比较像我之前搬过的一道原题,应该是(bfs)加上一些优化,感觉码量可能会比较大就先丢了

T2的限制条件和一年前写过的某场CF的D题是一样的,就是求的不大一样,刚起来不大想推式子就又丢了

T3应该是个比较奇妙的性质题,一眼会(O(n^2)),再要优化的话应该是要优化询问区间和(check)方式,不太想想就丢了

爆零预定

最终决定顺序开题,T1感觉就是一个最短路,8:25开始码,磕磕碰碰码了半个小时写完了,随意debug了一下就放在一边了

出去吃了个早饭(顺便%了一下准高三大佬hokiplus),路上大致发现T3只需要(checkO(n))个区间就可以了,但是还是只会(O(n^2))(check)

回来之后开(T2),期间有个地方想错了卡了一会,其它都还好,码完+调完10:30了

又来看T3,先花10min写了个(60pts),后来感觉会满分了,被一个奥妙重重的边界卡了一下,(11:20)写完了

jzoj这回卡了48面评测

吃完饭回来一看T3(fst)了,发现把(sum[r])写成了(sum[r+1]),改完就A了,十分难受

还是要提高代码能力啊

Solution

T1

直接bfs不知道多少分啊(我可能还不会直接bfs)

考虑最短路,首先四联通的点连边,接下来考虑传送门的影响:可以使某一个点到达上下左右最近的墙,花费是到达距离这个点最近的墙的时间

建完图直接最短路就好了

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii; 
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 1061109567
#define eps 1e-8
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
struct node{int to,nxt,cost;}sq[2002000];
int all=0,head[250010];
struct hnode{int u,dis;};
bool operator <(hnode p,hnode q){return p.dis>q.dis;}
priority_queue<hnode> hp;
int n,m,L[510][510],R[510][510],U[510][510],D[510][510],mind[510][510];
int id[510][510],dis[250010],st,ed,tot=0;
bool vis[250010];
char s[510][510];
 
int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

void bfs()
{
	queue<pii> q;
	rep(i,1,n) rep(j,1,m)
		if (s[i][j]=='#') q.push(mp(i,j));
	while (!q.empty())
	{
		int x=q.front().fir,y=q.front().sec;q.pop();
		rep(i,0,3)
		{
			int nx=x+dx[i],ny=y+dy[i];
			if ((nx>1) && (nx<=n) && (ny>1) && (ny<=m) && (s[nx][ny]=='.') && (!mind[nx][ny]))
			{
				mind[nx][ny]=mind[x][y]+1;q.push(mp(nx,ny));
			}
		}
	}
	rep(i,1,n) rep(j,1,m)
		if (s[i][j]=='.')
		{
			if (s[i][j-1]=='#') L[i][j]=id[i][j];
			else L[i][j]=L[i][j-1];
			if (s[i-1][j]=='#') U[i][j]=id[i][j];
			else U[i][j]=U[i-1][j];
		}
	per(i,n,1) per(j,m,1)
		if (s[i][j]=='.')
		{
			if (s[i][j+1]=='#') R[i][j]=id[i][j];
			else R[i][j]=R[i][j+1];
			if (s[i+1][j]=='#') D[i][j]=id[i][j];
			else D[i][j]=D[i+1][j];
		}
}

void add(int u,int v,int w)
{
	all++;sq[all].to=v;sq[all].nxt=head[u];sq[all].cost=w;head[u]=all;
}

void build()
{
	rep(i,1,n) rep(j,1,m)
	{
		if (s[i][j]=='.')
		{
			rep(k,0,3)
			{
				int nx=i+dx[k],ny=j+dy[k];
				if (s[nx][ny]=='.') add(id[i][j],id[nx][ny],1);
			}
		}
		if (L[i][j]!=id[i][j]) add(id[i][j],L[i][j],mind[i][j]);
		if (R[i][j]!=id[i][j]) add(id[i][j],R[i][j],mind[i][j]);
		if (U[i][j]!=id[i][j]) add(id[i][j],U[i][j],mind[i][j]);
		if (D[i][j]!=id[i][j]) add(id[i][j],D[i][j],mind[i][j]);
	}
}

void dij()
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	hp.push((hnode){st,0});dis[st]=0;
	while (!hp.empty())
	{
		int u=hp.top().u;hp.pop();
		if (vis[u]) continue;vis[u]=1;
		for (int i=head[u];i;i=sq[i].nxt)
		{
			int v=sq[i].to;
			if (dis[v]>dis[u]+sq[i].cost)
			{
				dis[v]=dis[u]+sq[i].cost;
				if (!vis[v]) hp.push((hnode){v,dis[v]});
			}
		}
	}
}
		
int main()
{
	freopen("cell.in","r",stdin);
	freopen("cell.out","w",stdout);
	n=read();m=read();
	rep(i,1,n) scanf("%s",s[i]+1);
	rep(i,1,n) rep(j,1,m) id[i][j]=(++tot); 
	rep(i,1,n) rep(j,1,m)
		if (s[i][j]=='C') {st=id[i][j];s[i][j]='.';}
		else if (s[i][j]=='F') {ed=id[i][j];s[i][j]='.';}
	bfs();build();dij();
	if (dis[ed]<maxd) printf("%d",dis[ed]);
	else puts("no");
	return 0;
}

所以T1的码量可能是T2+T3是什么鬼

T2

如果你写过这个题的话T2就比较简单了

根据BST的性质肯定是要按照(key)值排个序的,预处理一下任意两个点之间是否能连边

接下来考虑(dp),暴力dp的话就应该是记(dp_{i,j,k})表示把区间([i,j])建成以(k)为根的(BST)时的答案,这样(dp)好像是(O(n^5))的不大行,

注意到一棵(BST)的左右子树是互相独立的,于是可以记(f_{i,j,0})表示区间([i,j])将会作为(i-1)的右子树的答案,(f_{i,j,1})表示([i,j])作为(j+1)的左子树的答案,转移的话枚举(i-1)或者(j+1)的直系儿子是哪一个就可以了,时间是(O(n^3))

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define int long long
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define ky first
#define val second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
int n,dp[310][310][2],sum[310];
pii a[310];
int g[310][310];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int gcd(int x,int y)
{
	if (!y) return x;
	else return gcd(y,x%y);
}

signed main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();
	rep(i,1,n) 
	{
		a[i].ky=read();a[i].val=read();
	}
	sort(a+1,a+1+n);
	rep(i,1,n) rep(j,1,n) g[i][j]=gcd(a[i].ky,a[j].ky);
	rep(i,1,n) sum[i]=sum[i-1]+a[i].val;
	memset(dp,-0x3f,sizeof(dp));
	rep(i,1,n)
	{
		if ((i>1) && (g[i][i-1]!=1)) dp[i][i][0]=a[i].val;
		if ((i<n) && (g[i][i+1]!=1)) dp[i][i][1]=a[i].val;
	}
	ll ans=dp[0][0][0];
	rep(len,2,n)
	{
		for (int i=1;i+len-1<=n;i++)
		{
			int j=i+len-1;
			rep(k,i,j)
			{
				ll tmp;
				if (k==i) tmp=dp[i+1][j][0]+sum[j]-sum[i-1];
				else if (k==j) tmp=dp[i][j-1][1]+sum[j]-sum[i-1];
				else tmp=dp[i][k-1][1]+dp[k+1][j][0]+sum[j]-sum[i-1];
				if ((i>1) && (g[k][i-1]!=1)) dp[i][j][0]=max(tmp,dp[i][j][0]);
				if ((j<n) && (g[k][j+1]!=1)) dp[i][j][1]=max(tmp,dp[i][j][1]);
				if (len==n) ans=max(ans,tmp);
			}
		}
	}
	if (ans<0) puts("-1");
	else printf("%lld",ans);
	return 0;
}

T3

30pts应该是暴力枚举区间同时暴力(check)(O(n^3))

60pts就是枚举进行旋转区间的最中间的元素(即旋转中心),这样的话可以将一部分(check)放到一起,(O(n^2))

满分的话首先考虑到这样一件事情,合法的区间可以被优化到(O(n))级别,因为假设当前有一个旋转区间([l,r]),若(a_l eq r)(a_r eq l)的话,我们交换(l,r)就是无意义的,于是整个区间都可以往里面缩,最后我们枚举的区间肯定是形如([i,a_i])([a_i,i])

接下来就是优化(check),借鉴(60pts)的思想,我们把相同旋转中心的区间放在一起,按照长度排序,假设我们当前枚举的旋转区间是([l,r]),那么答案就是(sum(1,l-1)+nowsum(l,r)+sum(r+1,n))(sum(i,j))表示([i,j])中的合法点数,注意到(nowsum)的值的增加只有可能是因为旋转,假设这是按照长度排序的第(k)个区间,那么(nowsum(i,j)=k)(即使当前(l,r)交换对答案有2的贡献,我们也会在枚举下一个区间的时候考虑到)

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
int n,a[100100],pos[100100],sum[100100];
vector<int> rotlen[200200];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

void solve1()
{
	int sum=0;
	rep(i,1,n) 
	{
		a[i]=read();sum+=(a[i]==i);
	}
	int ans=sum;
	rep(i,1,n)
	{
		int l=i-1,r=i+1,tmp=sum;
		while ((l>=1) && (r<=n))
		{
			if (a[l]==r) tmp++;
			if (a[r]==l) tmp++;
			if (a[l]==l) tmp--;
			if (a[r]==r) tmp--;
			ans=max(tmp,ans);
			l--;r++;
		}
	}
	rep(i,1,n)
	{
		int l=i-1,r=i,tmp=sum;
		while ((l>=1) && (r<=n))
		{
			if (a[l]==r) tmp++;
			if (a[r]==l) tmp++;
			if (a[l]==l) tmp--;
			if (a[r]==r) tmp--;
			ans=max(tmp,ans);
			l--;r++;
		}
	}
	printf("%d",ans);
}

void solve2()
{
	rep(i,1,n)
	{
		a[i]=read();sum[i]=sum[i-1];
		if (a[i]==i) sum[i]++;
	}
	rep(i,1,n)
		rotlen[i+a[i]].pb(abs(i-a[i])+1);
	int ans=sum[n];
	rep(i,1,n*2)
	{
		if (!rotlen[i].size()) continue;
		sort(rotlen[i].begin(),rotlen[i].end());
		int len=rotlen[i].size();
		rep(j,0,len-1)
		{
			int now=rotlen[i][j],l,r;
			if (i%2==0)
			{
				l=i/2-now/2;r=i/2+now/2;
			}
			else
			{
				l=i/2-now/2+1;
				r=i/2+now/2;
			}
			ans=max(ans,j+1+sum[l-1]+sum[n]-sum[r]);
		}
	}
	printf("%d",ans);
}

int main()
{
	//freopen("rotate.in","r",stdin);
	//freopen("rotate.out","w",stdout);
	n=read();
	solve2();
	return 0;
}
原文地址:https://www.cnblogs.com/encodetalker/p/11327409.html