加载中…

返回上一页

2022NOIP A层联测25

下发文件(密码为原 accoders 比赛密码)

惊喜二十二

没改.

K-构造

要求构造一个序列,使得子序列中和等于 0 的个数等于 K.

具体的话考虑如何去构造:当一个数加入的时候,其会与其后面与它和成相反数的序列产生贡献.

所以,对于每一个新加进来的数,就去判断它对于整个答案的贡献是多少.

由于这是构造题,直接暴力把一每个数搜出来,然后对于每个数硬判断就行了.

一组合法的示例:

点击展开

constr1.out

5
-4 -3 -1 3 4

constr2.out

8
-5 -4 -3 1 4 4 4 8

constr3.out

9
-3 -2 -2 1 2 1 2 1 1

constr4.out

11
-1 -1 -1 -1 -1 1 1 1 1 1 5

constr5.out

21
-47 -39 -9 1 1 2 2 2 4 4 4 4 5 5 5 7 7 8 8 9 9

constr6.out

20
-40 -45 -3 5 5 7 2 8 6 8 4 5 5 1 5 5 3 7 2 5

constr7.out

12
-8 -8 -3 2 1 1 1 7 7 3 2 6

constr8.out

16
-15 -10 -13 6 7 3 3 2 6 5 3 4 8 4 1 1

constr9.out

23
-59 -59 -3 1 9 5 4 9 6 4 7 9 4 2 4 9 4 5 5 4 6 6 9

constr10.out

26
-45 -54 -10 3 7 4 6 5 3 9 2 2 1 4 4 7 1 9 7 6 3 3 5 1 5 9

constr11.out

26
-54 -1 -2 5 5 5 7 8 7 9 2 4 7 4 7 4 3 7 7 3 5 1 4 1 8 7

constr12.out

26
-42 -3 -6 5 6 7 2 7 5 1 2 7 6 1 2 2 3 8 9 6 4 1 5 8 3 1

有耐心应该能搜出来

eafoo 有一种类似背包的做法,大致是通过随机化手段,先生成一组随机值,然后累和,从第一个往后遍历,找到第一个能够构造出解的构造并输出.

函数的权力

把该进制下每一位的数字都存一下. 对于每一位,如果这一位是 0,那么对答案是没有贡献的,直接跳过. 否则,当前的答案为前面所有位的和加上当前位与 k - 1 的乘积.

如果答案合法(另外记录一个 mx 表示当前能到的最大的 ans3,它应大于等于 l),那么就更新 ans. 如果 ans 改变了,ans2ans3 也会相应地改变. 否则就取最值.

点击查看代码
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 71
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll t,k,l,r,sum,mx,ans,ans2,ans3,pw[maxn];
vector<ll> g;
int main()
{
	freopen("powerf.in","r",stdin); freopen("powerf.out","w",stdout);
	pw[0]=1; t=read(); while(t--)
	{
		ans=-1;g.clear(); k=read();l=read();r=read(); while(r) g.emplace_back(r%k),r/=k; sum=(ll)g.size()-2;mx=0;
		for(rll i=1;i<maxn;i++) pw[i]=pw[i-1]*k;
		for(rll i=(ll)g.size()-1;~i;i--) if(g[i])
		{
			rll a=sum+g[i]-1+(k-1)*i; if(i==(ll)g.size()-1&&g[i]==1) a--;// 注意特判边界
			rll b=mx+g[i]*pw[i]-1;
			if(b>=l) { if(ans<a) ans=a,ans2=ans3=b; else if(ans==a) ans2=min(ans2,b),ans3=max(ans3,b); }
			sum+=g[i];mx+=g[i]*pw[i];// cout<<sum<<' '<<mx<<endl;
		}
		if(mx>=l) { if(ans<sum) ans=sum,ans2=ans3=mx; else if(ans==sum) ans2=min(ans2,mx),ans3=max(ans3,mx); }
		write(ans),put_,write(ans2),put_,write(ans3),putn;
	}
	return 0;
}

最大可达流形

建议别学我这种做法,否则你会思考自己是否还是自己.

对于每一种情况,对它广搜,记录每个点的 belong 用于判断能否相互到达.

然后,记录一下每个点能放的最大的正方形的边长,对于相邻且相同的,再进行广搜,它们又构成了一个连通块(和上面的那个不同). 然后对于每次询问对连通块跑最短路.

这样就能拿到 95 分.

点击查看代码
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define ull unsigned ll
#define pll pair<ll,ll>
#define maxn 2001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll dx[4][2]={ { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 } };
ll n,m,mx[maxn][maxn],bel[maxn][maxn],bel2[maxn][maxn],tot,tot2,mx1[30001];
char mp[maxn][maxn];
vector<ll> dis[30001];
unordered_set<ll> g[30001];
bool vis;
queue<pll> q;
priority_queue<pll> que;
#define hs(x,y) ((ull)x*19260817+y)
inline void dij(rll x)
{
	for(rll i=1;i<=tot2;i++) dis[x].emplace_back(0); dis[x][x-1]=mx1[x]; que.push((pll) { mx1[x],x });
	// cout<<"*1\n";
	while(!que.empty())
	{
		rll t=que.top().second;que.pop(); for(rg auto to:g[t])
			if(dis[x][to-1]<min(dis[x][t-1],mx1[to])) que.push((pll) { dis[x][to-1]=min(dis[x][t-1],mx1[to]),to });
	}
	// cout<<"*2\n";
}
int main()
{
	freopen("planem.in","r",stdin); freopen("planem.out","w",stdout);
	// freopen("in.txt","r",stdin); freopen("out.txt","w",stdout);
	n=read(); for(rll i=1;i<=n;i++) scanf("%s",mp[i]+1);
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='#') { vis=1;break; }
	if(!vis)
	{
		fprintf(stderr,"进入了特判,连通块个数:1\n");
		m=read(); while(m--)
		{
			rll x1=read(),y1=read(),x2=read(),y2=read();
			write((min(x1-1,min(y1-1,min(x2-1,min(y2-1,min(n-x1,min(n-x2,min(n-y1,n-y2)))))))<<1)+1),putn;
		}
		return 0;
	}
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) mx[i][j]=mp[i][j]=='.'?INT_MAX:0;
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) mx[i][j]=min(mx[i][j],min(mx[i-1][j-1],min(mx[i-1][j],min(mx[i-1][j+1],mx[i][j-1])))+1);
	for(rll i=n;i;i--) for(rll j=n;j;j--) mx[i][j]=min(mx[i][j],min(mx[i+1][j-1],min(mx[i+1][j],min(mx[i+1][j+1],mx[i][j+1])))+1);
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mx[i][j]) mx[i][j]=(mx[i][j]<<1)-1;
	// for(rll i=1;i<=n;i++) { for(rll j=1;j<=n;j++) write(mx[i][j]),put_;putn; } putn;
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='.'&&(!bel[i][j]))
	{
		bel[i][j]=++tot; q.push((pll) { i,j });
		while(!q.empty())
		{
			rll x=q.front().first,y=q.front().second; q.pop();
			for(rll k=0;k<4;k++)
			{
				rll tx=x+dx[k][0],ty=y+dx[k][1]; if((!tx)||tx>n||(!ty)||ty>n||mp[tx][ty]=='#'||bel[tx][ty]) continue;
				bel[tx][ty]=tot; q.push((pll) { tx,ty });
			}
		}
	}
	// for(rll i=1;i<=n;i++) { for(rll j=1;j<=n;j++) write(bel[i][j]),put_;putn; }
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='.'&&(!bel2[i][j]))
	{
		bel2[i][j]=++tot2; mx1[tot2]=mx[i][j]; q.push((pll) { i,j });
		while(!q.empty())
		{
			rll x=q.front().first,y=q.front().second; q.pop();
			for(rll k=0;k<4;k++)
			{
				rll tx=x+dx[k][0],ty=y+dx[k][1]; if((!tx)||tx>n||(!ty)||ty>n||(mx[tx][ty]^mx[x][y])||bel2[tx][ty]) continue;
				bel2[tx][ty]=tot2; q.push((pll) { tx,ty });
			}
		}
	}
	fprintf(stderr,"连通块个数:%d\n",tot2);
	// for(rll i=1;i<=n;i++) { for(rll j=1;j<=n;j++) write(bel2[i][j]),put_;putn; } write(tot2);putn;
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='.') for(rll k=0;k<4;k++)
	{
		rll tx=i+dx[k][0],ty=j+dx[k][1];
		if((!tx)||tx>n||(!ty)||ty>n||mp[tx][ty]=='#'||bel2[i][j]==bel2[tx][ty]) continue; g[bel2[i][j]].insert(bel2[tx][ty]);
	}
	fprintf(stderr,"建边已完成");
	// for(rll i=1;i<=tot2;i++) { write(i);putn; for(auto j:g[i]) write(j),put_;putn; }
	m=read(); for(rll i=1;i<=m;i++)
	{
		rll x1=read(),y1=read(),x2=read(),y2=read(); if(bel[x1][y1]^bel[x2][y2]) { puts("0"); continue; }
		if(mx[x1][y1]==1||mx[x2][y2]==1) { puts("1");continue; }
		if(bel2[x1][y1]==bel2[x2][y2]) { write(mx[x1][y1]);putn;continue; }
		if(!dis[bel2[x1][y1]].empty()) { write(dis[bel2[x1][y1]][bel2[x2][y2]-1]);putn;continue; }
		if(!dis[bel2[x2][y2]].empty()) { write(dis[bel2[x2][y2]][bel2[x1][y1]-1]);putn;continue; }
		if(x1>x2) swap(x1,x2),swap(y1,y2); fprintf(stderr,"进入了最短路,bel = %d\n",bel2[x1][y1]);
		dij(bel2[x1][y1]);// cout<<bel2[x1][y1]<<' '<<bel2[x2][y2]<<endl;
		write(dis[bel2[x1][y1]][bel2[x2][y2]-1]);putn;
	}
	return 0;
}

然后发现超时的那个点的连通块个数极多,导致每次最短路耗时很长. 对于这类数据,可以使用一个并查集来维护,每次合并已遍历过的点,离线处理答案,这样就能快速地找到对应的最窄大小了.

点击查看代码
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define ull unsigned ll
#define pll pair<ll,ll>
#define maxn 2001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10);putchar(x%10|'0'); }
ll dx[4][2]={ { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 } };
ll n,m,mx[maxn][maxn],bel[maxn][maxn],bel2[maxn][maxn],tot,tot2,mx1[30001],ans[300001];
char mp[maxn][maxn];
vector<ll> dis[30001];
vector<pll> h[maxn*(maxn+1)];
unordered_set<ll> g[30001];
bool vis;
queue<pll> q;
priority_queue<pll> que;
ll fa[maxn*(maxn+1)];
#define id(x,y) (x*n+y)
inline ll find(rll x) { if(x^fa[x]) fa[x]=find(fa[x]); return fa[x]; }
inline void dij(rll x)
{
	for(rll i=1;i<=tot2;i++) dis[x].emplace_back(0); dis[x][x-1]=mx1[x]; que.push((pll) { mx1[x],x });
	// cout<<"*1\n";
	while(!que.empty())
	{
		rll t=que.top().second;que.pop(); for(rg auto to:g[t]) if(dis[x][to-1]<min(dis[x][t-1],mx1[to]))
			que.push((pll) { dis[x][to-1]=min(dis[x][t-1],mx1[to]),to });
	}
	// cout<<"*2\n";
}
inline void merge(rll x,rll y,rll k)
{
	x=find(x),y=find(y);
	if(x==y) return;// cout<<x<<' '<<y<<endl;
	if(h[x].size()<h[y].size()) swap(x,y);
	for(rll i=0;i<h[y].size();i++) if(find(h[y][i].first)==x) ans[h[y][i].second]=k;// ,cout<<h[y][i].second<<" "<<k<<endl;
	for(rll i=0;i<h[y].size();i++) h[x].push_back(h[y][i]); fa[y]=x;h[y].clear();
}
inline void spj()
{
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) g[mx[i][j]].insert(id(i,j));
	for(rll i=1;i<maxn*(maxn+1);i++) fa[i]=i;
	m=read(); for(rll i=1;i<=m;i++)
	{
		rll x1=read(),y1=read(),x2=read(),y2=read(); if(x1==x2&&y1==y2) ans[i]=mx[x1][y1];
		else h[id(x1,y1)].push_back((pll) { id(x2,y2),i }),h[id(x2,y2)].push_back((pll) { id(x1,y1),i });
	}
	for(rll k=n+1>>1;k;k--) for(auto i:g[(k<<1)-1])
	{
		rll y=(i-1)%n+1,x=(i-y)/n;// cout<<i<<' '<<x<<' '<<y<<endl;
		for(rll j=0;j<4;j++)
		{
			rll nx=x+dx[j][0],ny=y+dx[j][1];
			if(nx&&nx<=n&&ny&&ny<=n&&mx[nx][ny]>=(k<<1)-1) assert(x),merge(id(x,y),id(nx,ny),(k<<1)-1);
		}
	}
	for(auto i:g[0])
	{
		rll y=(i-1)%n+1,x=(i-y)/n;// cout<<i<<' '<<x<<' '<<y<<endl;
		for(rll j=0;j<4;j++)
		{
			rll nx=x+dx[j][0],ny=y+dx[j][1];
			if(nx&&nx<=n&&ny&&ny<=n&&mx[nx][ny]>=0) assert(x),merge(id(x,y),id(nx,ny),0);
		}
	}
	for(rll i=1;i<=m;i++) write(ans[i]),putn;
}
int main()
{
	freopen("planem.in","r",stdin); freopen("planem.out","w",stdout);
	// freopen("in.txt","r",stdin); freopen("out.txt","w",stdout);
	n=read(); for(rll i=1;i<=n;i++) scanf("%s",mp[i]+1);
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='#') { vis=1;break; }
	if(!vis)
	{
		fprintf(stderr,"进入了特判,连通块个数:1\n");
		m=read(); while(m--)
		{
			rll x1=read(),y1=read(),x2=read(),y2=read();
			write((min(x1-1,min(y1-1,min(x2-1,min(y2-1,min(n-x1,min(n-x2,min(n-y1,n-y2)))))))<<1)+1),putn;
		}
		return 0;
	}
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) mx[i][j]=mp[i][j]=='.'?INT_MAX:0;
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) mx[i][j]=min(mx[i][j],min(mx[i-1][j-1],min(mx[i-1][j],min(mx[i-1][j+1],mx[i][j-1])))+1);
	for(rll i=n;i;i--) for(rll j=n;j;j--) mx[i][j]=min(mx[i][j],min(mx[i+1][j-1],min(mx[i+1][j],min(mx[i+1][j+1],mx[i][j+1])))+1);
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mx[i][j]) mx[i][j]=(mx[i][j]<<1)-1;
	// for(rll i=1;i<=n;i++) { for(rll j=1;j<=n;j++) write(mx[i][j]),put_;putn; } putn;
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='.'&&(!bel[i][j]))
	{
		bel[i][j]=++tot; q.push((pll) { i,j });
		while(!q.empty())
		{
			rll x=q.front().first,y=q.front().second; q.pop();
			for(rll k=0;k<4;k++)
			{
				rll tx=x+dx[k][0],ty=y+dx[k][1]; if((!tx)||tx>n||(!ty)||ty>n||mp[tx][ty]=='#'||bel[tx][ty]) continue;
				bel[tx][ty]=tot; q.push((pll) { tx,ty });
			}
		}
	}
	// for(rll i=1;i<=n;i++) { for(rll j=1;j<=n;j++) write(bel[i][j]),put_;putn; }
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='.'&&(!bel2[i][j]))
	{
		bel2[i][j]=++tot2; mx1[tot2]=mx[i][j]; q.push((pll) { i,j });
		while(!q.empty())
		{
			rll x=q.front().first,y=q.front().second; q.pop();
			for(rll k=0;k<4;k++)
			{
				rll tx=x+dx[k][0],ty=y+dx[k][1]; if((!tx)||tx>n||(!ty)||ty>n||(mx[tx][ty]^mx[x][y])||bel2[tx][ty]) continue;
				bel2[tx][ty]=tot2; q.push((pll) { tx,ty });
			}
		}
	}
	fprintf(stderr,"连通块个数:%d\n",tot2);
	if(tot2>10000) { fprintf(stderr,"进入了合并阶段\n"); spj(); return 0; }
	// for(rll i=1;i<=n;i++) { for(rll j=1;j<=n;j++) write(bel2[i][j]),put_;putn; } write(tot2);putn;
	for(rll i=1;i<=n;i++) for(rll j=1;j<=n;j++) if(mp[i][j]=='.') for(rll k=0;k<4;k++)
	{
		rll tx=i+dx[k][0],ty=j+dx[k][1];
		if((!tx)||tx>n||(!ty)||ty>n||mp[tx][ty]=='#'||bel2[i][j]==bel2[tx][ty]) continue; g[bel2[i][j]].insert(bel2[tx][ty]);
	}
	fprintf(stderr,"建边已完成\n");
	// for(rll i=1;i<=tot2;i++) { write(i);putn; for(auto j:g[i]) write(j),put_;putn; }
	m=read(); for(rll i=1;i<=m;i++)
	{
		rll x1=read(),y1=read(),x2=read(),y2=read(); if(bel[x1][y1]^bel[x2][y2]) { puts("0"); continue; }
		if(mx[x1][y1]==1||mx[x2][y2]==1) { puts("1");continue; }
		if(bel2[x1][y1]==bel2[x2][y2]) { write(mx[x1][y1]);putn;continue; }
		if(!dis[bel2[x1][y1]].empty()) { write(dis[bel2[x1][y1]][bel2[x2][y2]-1]);putn;continue; }
		if(!dis[bel2[x2][y2]].empty()) { write(dis[bel2[x2][y2]][bel2[x1][y1]-1]);putn;continue; }
		if(x1>x2) swap(x1,x2),swap(y1,y2);
		// fprintf(stderr,"进入了最短路,bel = %d\n",bel2[x1][y1]);
		dij(bel2[x1][y1]);// cout<<bel2[x1][y1]<<' '<<bel2[x2][y2]<<endl;
		write(dis[bel2[x1][y1]][bel2[x2][y2]-1]);putn;
	}
	return 0;
}

正解其实是 kruskal 重构树上求 lca,由于没有非常明白原理,先暂且不写了.

posted @ 2022-11-11 19:34  1Liu  阅读(39)  评论(1编辑  收藏  举报