ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

5.5 省选模拟赛 B Permutation 构造 贪心

2020-05-06 19:55:27  阅读:260  来源: 互联网

标签:10 5.5 get 省选 rep int MAXN Permutation put


LINK:Permutation

avatar
avatar

对于这种构造神题 我自然是要补的。为啥就我没想出来哇.

30分还是很好写的 注意8!实际上很小 不需要爆搜 写bfs记录状态即可。至于判断状态是否出现与否 可以开map np一点的做法是利用康拓展开和逆康托展开(还需要hash 没啥用.

但是 经过不断的手玩排列为8的数列容易发现 对于最差的情况 8 7 6 5 4 3 2 1 也最多需要3次。

所以 可以直接去掉bfs的过程 直接三层2^n枚举 开map统计状态量 这样可以做的飞快。算法的名称估计可以叫做为 模拟bfs.

const int MAXN=41000;
int n,T;
int ans;
int a[MAXN],ans1,ans2,ans3;
int s1[MAXN],s2[MAXN],s3[MAXN],p[MAXN],pre2[MAXN],pre3[MAXN];
map<int,int>H[4];
inline void put_(int x,char c){printf("%d%c",x,c);}
inline void put1()
{
	put(ans);
	if(T)
	{
		rep(1,n,i)put_(a[i],i==n?'\n':' ');
		if(ans1)rep(1,n,i)put_(s1[ans1]/p[i]%10,i==n?'\n':' ');
		if(ans2)rep(1,n,i)put_(s2[ans2]/p[i]%10,i==n?'\n':' ');
		if(ans3)rep(1,n,i)put_(s3[ans3]/p[i]%10,i==n?'\n':' ');
		return;
	}
}
int main()
{
	//freopen("1.in","r",stdin);
	get(n);get(T);
	int ss=0,ww=0;p[n]=1;
	rep(1,n,i)get(a[i]),ss=ss*10+a[i];
	rep(1,n,i)ww=ww*10+i;
	fep(n-1,1,i)p[i]=p[i+1]*10;
	if(ss==ww){put1();return 0;}
	if(n<=8)
	{
		int maxx=(1<<n)-1;
		int id1=0;++ans;
		rep(1,maxx,i)
		{
			int w1=0,w2=0,cnt2=0;
			rep(1,n,j)
			{
				int cc=ss/p[j]%10;
				if(i&(1<<(j-1)))w2=w2*10+cc,++cnt2;
				else w1=w1*10+cc;
			}
			w1=w1*p[n-cnt2]+w2;
			s1[++id1]=w1;H[1][w1]=1;
			if(w1==ww){ans1=id1;put1();return 0;}
		}
		int id2=0;++ans;
		rep(1,maxx,i)
		{
			rep(1,id1,k)
			{
				int w1=0,w2=0,cnt2=0;
				rep(1,n,j)
				{
					int cc=s1[k]/p[j]%10;
					if(i&(1<<(j-1)))w2=w2*10+cc,++cnt2;
					else w1=w1*10+cc;
				}
				w1=w1*p[n-cnt2]+w2;
				if(H[1].find(w1)!=H[1].end()||H[2].find(w1)!=H[2].end())continue;
				s2[++id2]=w1;pre2[id2]=k;H[2][w1]=1;if(w1==ww){ans2=id2;ans1=pre2[id2];put1();return 0;}
			}
		}
		int id3=0;++ans;
		rep(1,maxx,i)
		{
			rep(1,id2,k)
			{
				int w1=0,w2=0,cnt2=0;
				rep(1,n,j)
				{
					int cc=s2[k]/p[j]%10;
					if(i&(1<<(j-1)))w2=w2*10+cc,++cnt2;
					else w1=w1*10+cc;
				}
				w1=w1*p[n-cnt2]+w2;
				if(H[1].find(w1)!=H[1].end()||H[2].find(w1)!=H[2].end()||H[3].find(w1)!=H[3].end())continue;
				s3[++id3]=w1;pre3[id3]=k;H[3][w1]=1;
				if(w1==ww)
				{ans2=pre3[id3];ans1=pre2[ans2];ans3=id3;put1();return 0;}
			}
		}
	}
	return 0;
}

值得注意的是 代码写的过于繁琐 应该写一个函数调用三次 这样可以简便很多.

接下来是考虑正解:观察数据范围 其要求我们最多进行20次这样的操作。

这个下界类似于logn 如何确定下界:每一次都可以将两端进行合并 这样合并的条件两端是递增的 且第一段的最大值<第二段的最小值。

且这两端合并之后数值应该是连续的 不连续就没有意义。

基于数值连续这个思想我们可以设出一个q数组\(q_x\)表示x这个值所处位置。

每次合并两端数值相近的 如果有多段我们可以进行同时合并 所以最多此时为log段数上取整。

还有一种证明方法是 对于q数组 我们把这个数组划分若干段 每一段是连续的极长递增的段。

考虑任意两端 如果每次操作都相同那么他们一定还是无法进行合并 是不合法的。

所以对于任意两端 要求他们的操作序列是不相同的 所以要产生2^k种不同的操作序列才可以还原 答案仍然是logn.

至此 得到答案的下界之后 尝试构造。

构造也很容易了 因为我们知道每次只要合并两段即可 这样最多会合并logn次。

一种比较好写的写法 将每段划分奇偶 下标从0开始.

那么显然 奇数段放1偶数段放0 就可以将两端合并了 下次进行时容易发现下标可以直接/2算出 比暴力标号常数小一倍.

const int MAXN=50010;
int cnt,T,n;
int b[MAXN],a[MAXN],c[MAXN],ql[MAXN],qr[MAXN];
int main()
{
	freopen("1.in","r",stdin);
	get(n);get(T);
	rep(1,n,i)get(a[i]),b[a[i]]=i;
	rep(1,n,i)if(b[i]>b[i-1])c[i]=c[i-1];
	else c[i]=++cnt;
	int w=0;while((1<<w)<cnt+1)++w;
	put(w);
	if(!T)return 0;
	rep(1,n,i)put_(a[i]);puts("");
	rep(0,w-1,i)
	{
		int sl=0,sr=0;
		rep(1,n,j)if(!((c[a[j]]>>i)&1))ql[++sl]=a[j];
		else qr[++sr]=a[j];
		rep(1,sl,j)a[j]=ql[j];
		rep(1,sr,j)a[j+sl]=qr[j];
		rep(1,n,i)put_(a[i]);puts("");
	}
	return 0;
}

标签:10,5.5,get,省选,rep,int,MAXN,Permutation,put
来源: https://www.cnblogs.com/chdy/p/12838486.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有