1. 程式人生 > 其它 >CF1392G Omkar and Pies【狀壓DP】

CF1392G Omkar and Pies【狀壓DP】

CF1392G Omkar and Pies

Description

給定兩個長度為 \(k\)\(01\) 串,分別是起始串和目標串。

依次給出 \(n\) 個可選操作,第 \(j\) 個操作 \(a_j,b_j\) 表示交換起始串的 \(a_j\) 位置與 \(b_j\) 位置。

你需要選擇操作序列中其中連續的一段操作依次按順序執行,並且選擇的運算元量不小於 \(m\)

你需要讓操作完成後的串與目標串對應相同的位置數量儘可能多。

輸出最大數量與你選取操作的區間(若有多種方案輸出任意一個即可)。

\(k\le 20,m\le n\le 10^6\)

Solution

首先執行 \([l,r]\)

的區間操作可以轉化為,對起始串進行 \([l,n]\) 操作,再對目標串進行 \([r+1,n]\) 操作,這樣一來 \([r+1,n]\) 的操作相當於被抵消了,就跟原問題一樣了。

首先可以 \(\mathcal O(nk)\) 預處理出對起始串與目標串執行所有後綴操作得到的序列,設分別為 \(\{a_n\}\)\(\{b_n\}\)

現在我們等於需要最大化 \(a_i\)\(b_j\) 的相同字元數量,並使 \(j-i\ge m\),設 \(x\)\(a_i\)\(1\) 的個數,\(y\)\(b_j\)\(1\) 的個數,\(z\)\(a_i\)

\(b_j\) 同時為 \(1\) 的位置數量,因此對應相同的位置數量就等於:

\[z+(k-x-y+z)=2z+k-x-y \]

由於 \(x,y\) 是確定的,因此只需要最大化 \(z\)。列舉最終公共 \(1\) 的位置為 \(s\),求出:

\[dp_{0,s}=\min i[s\subset a_i]\\ dp_{1,s}=\max i[s\subset b_i] \]

如果 \(dp_{1,s}-dp_{0,s}\ge m\) ,就可以用 \(popcount(s)\) 來更新答案了,於是預處理 \(a,b,dp\) 的複雜度都是 \(\mathcal O(nk)\)。總複雜度為 \(\mathcal O(nk)\)

,可以通過此題。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+20;
int n,m,k,a,b,L[N],R[N],c[N],f[N],g[N],tmp[N][21],ret[21];
char s[21],t[21];
int main(){
//	freopen("option2.in","r",stdin);
	scanf("%d%d%d",&n,&m,&k);
	scanf("%s",s+1);
	int cta=0,ctb=0;
	for(int i=1;i<=k;++i){
		a=(a<<1)+s[i]-'0';
		if(s[i]=='1') cta++;
	}
	scanf("%s",t+1);
	for(int i=1;i<=k;++i){
		b=(b<<1)+t[i]-'0';
		if(t[i]=='1') ctb++;
	}
	for(int i=0;i<(1<<k);++i) f[i]=n+1,g[i]=0;
	for(int i=1;i<=n;++i) scanf("%d%d",&L[i],&R[i]);
	for(int i=1;i<=k;++i) ret[i]=i;
	for(int i=n;i>=1;--i){
		for(int j=1;j<=k;++j) tmp[i][j]=j;
		swap(tmp[i][L[i]],tmp[i][R[i]]);
		for(int j=1;j<=k;++j) ret[j]=tmp[i][ret[j]];
		a=0;
		for(int j=1;j<=k;++j) a=(a<<1)+s[ret[j]]-'0';
		f[a]=min(f[a],i);
	}
	g[b]=n+1;
	for(int i=1;i<=k;++i) ret[i]=i;
	for(int i=n;i>=1;--i){
		for(int j=1;j<=k;++j) tmp[i][j]=j;
		swap(tmp[i][L[i]],tmp[i][R[i]]);
		for(int j=1;j<=k;++j) ret[j]=tmp[i][ret[j]];
		b=0;
		for(int j=1;j<=k;++j) b=(b<<1)+t[ret[j]]-'0';
		g[b]=max(g[b],i);
	}
	int ans=-1,reta=0,retb=0;
	for(int i=((1<<k)-1);~i;--i){
		int ct=0;
		for(int j=0;j<k;++j){
			if(i&(1<<j)){
				ct++;
				int tmp=i^(1<<j);
				f[tmp]=min(f[tmp],f[i]);
				g[tmp]=max(g[tmp],g[i]);
			}
		}
		if(g[i]-f[i]>=m){
			int ret=k-cta-ctb+ct+ct;
			if(ret>ans) ans=ret,reta=f[i],retb=g[i]-1;
		}
	}
	printf("%d\n%d %d",ans,reta,retb);
	return 0;
}