1. 程式人生 > 其它 >CF1392G Omkar and Pies 題解

CF1392G Omkar and Pies 題解

考慮如果把同樣的一段操作序列同時給兩個串做,對答案是沒有影響的。所以考慮把每個區間差分成字尾(不能是字首,因為這樣相同的操作序列在前面沒法抵消),這樣就可以表示區間了。兩個串分別的對每個字尾操作之後得到的串可以 \(O(nk)\) 算出來,就是記錄每個位置最終會被換到哪裡就可以從右向左遞推。

算出兩個串前後綴之後(設為 \(a_i,b_i\)),考慮如何計算答案:設 \(a,b\)\(1\) 的個數分別為 \(p,q\)\(a,b\) 重合的 \(1\) 的個數為 \(r\),那麼答案為 \(k-p-q+2r\)\(k,p,q\) 都是常數,現在只需要使 \(r\) 最大。注意到最終的情況只有 \(2^k\)

種,考慮直接列舉答案。設 \(f_S=\min\{i|S\subset a_i\},g_S=\max\{i|S\subset b_i\}\),對於每個 \(S\) 判斷 \(g_S-f_S\) 是否 \(\geq m\) 即可更新答案。對於 \(f,g\) 值只需要從大到小遞推一下就可以了,這部分複雜度是 \(O(2^k\cdot k)\)

所以總的複雜度為 \(O(nk+k2^k)\),可以通過此題。

點選檢視程式碼
const int N=1e6+13,M=(1<<20)+13,K=20+1;
int n,m,k,l[N],r[N],a[N],b[N],f[M],g[M];
int pos[N][K];
char ina[K],inb[K];
int main(){
//file();
	read(n),read(m),read(k);
	read(ina),read(inb);
	for(int i=1;i<=n;++i){
		read(l[i]),read(r[i]);
		--l[i],--r[i];
	}
	int x=0,y=0,p=0,q=0;
	for(int i=0;i<k;++i) p+=(ina[i]=='1'),q+=(inb[i]=='1'); 
	for(int i=0;i<k;++i) pos[n+1][i]=i;
	for(int i=n;i;--i){
		for(int j=0;j<k;++j){
			if(j==l[i]) pos[i][j]=pos[i+1][r[i]];
			else if(j==r[i]) pos[i][j]=pos[i+1][l[i]];
			else pos[i][j]=pos[i+1][j];
		}
	}
	for(int i=1;i<=n+1;++i){
		static int ta[K],tb[K];
		for(int j=0;j<k;++j)
			ta[pos[i][j]]=(ina[j]=='1'),tb[pos[i][j]]=(inb[j]=='1');
		for(int j=0;j<k;++j)
			a[i]=(a[i]<<1)+ta[j],b[i]=(b[i]<<1)+tb[j];
	}
	for(int i=0;i<(1<<k);++i) f[i]=n+1;
	for(int i=n+1;i;--i) f[a[i]]=i;
	for(int i=1;i<=n+1;++i) g[b[i]]=i;
	int ans=0,ansl=1,ansr=1;
	for(int s=(1<<k)-1;s>=0;--s){
		int cnt=0;
		for(int i=0;i<k;++i) cnt+=((s>>i)&1);
		if(cnt>ans&&g[s]-f[s]>=m) ans=cnt,ansl=f[s],ansr=g[s]-1;
		for(int i=0;i<k;++i){
			if(!((s>>i)&1)) continue;
			f[s^(1<<i)]=min(f[s^(1<<i)],f[s]);
			g[s^(1<<i)]=max(g[s^(1<<i)],g[s]);
		}
	}
	println(k-p-q+2*ans);print(ansl),print(' '),println(ansr);
	return 0;
}