CF1615F LEGOndary Grandmaster
对于两个长度为 \(n\) 的 \(01\) 串 \(s,t\) ,你可以对 \(s\) 进行两种操作:把相邻两个 \(0\) 变成 \(1\) 或把相邻两个 \(1\) 变成 \(0\) ,定义 \(s\) 到 \(t\) 的距离为最少操作次数使得 \(s\) 变成 \(t\) ,如过没法变则距离为 \(0\) 。
现在你有两个不完整的字符串,可以把其中的 \(?\) 变成 \(0\) 或 \(1\) ,求所有情况所得到的两个 \(01\) 串的距离之和。
不容易发现,如果我们将 \(s,t\) 串的偶数位置的数 \(01\) 互换(如果是问号不变),那么在原串中的操作就是交换新串两个位置的元素,如果两个位置原来不能变化,那么在新串中就表现为交换两个相同的元素,这显然是不优秀的,所以我们可以忽略。以上过程自己模拟一下就可以发现了。
那么两个串可以互相转化的充要条件就是两个串中 \(1\) 的个数相等。并且假设 \(s\) 串 \(t\) 串 \(1\) 的位置的数组为 \(p,q\)(\(p,q\) 中元素递增),那么答案其实就是 \(\sum |p_i-q_i|\)。
这其实并不好统计答案,因为不好处理问好的情况。
我们定义 \(a_i,b_i\) 表示 \(s[1,i]\) 中 \(1\) 的个数以及 \(t[1,i]\) 中 \(1\) 的个数,那么答案其实就是 \(\sum_i^n |a_i-b_i|\)。因为一次操作最多使得 \(|a_i-b_i|\) 减少 \(1\),因此需要 \(|a_i-b_i|\) 次操作才能得到 \(a_i=b_i\),操作的下界是 \(\sum_{i}^n|a_i-b_i|\)。(看不懂也可自己手玩一下)
然后就可以 DP了。
定义 \(f_{i,j}\) 为到第 \(i\) 个位置 \(a_i-b_i=j\) 的方案数,\(g_{i,j}\) 为到第 \(i\) 个位置 \(a_i-b_i=j\) 需要的操作总数。
\[f_{i,j+[s_i=1]-[t_i=1]}\gets f_{i-1,j}\\ g_{i,j+[s_i=1]-[t_i=1]}\gets g_{i-1,j}+f_{i-1,j}\times|j| \]注意是问号的时候显然要对 \(s_i=0,1\) 的情况均转移。
const int N=2003;
const ll MOD=1000000007;
int n;char s[N],t[N];
ll f[N][N*2],g[N][N*2];
inline void CFmode(){
n=read();
for(int i=0;i<=n;++i){
for(int j=0;j<=2*n;++j)
f[i][j]=g[i][j]=0;
}
scanf("%s%s",s+1,t+1);
for(int i=2;i<=n;i+=2)
if(s[i]!='?')s[i]=((s[i]-'0')^1)+'0';
for(int i=2;i<=n;i+=2)
if(t[i]!='?')t[i]=((t[i]-'0')^1)+'0';
int bas=n;
f[0][bas]=1;
for(int i=1;i<=n;++i){
for(int j=-i+1;j<=i-1;++j){
for(int a=0;a<=1;++a){
for(int b=0;b<=1;++b){
if((s[i]=='?'||s[i]=='0'+a)&&(t[i]=='?'||t[i]=='0'+b)){
g[i][j+a-b+bas]=(g[i][j+a-b+bas]+g[i-1][j+bas]+f[i-1][j+bas]*abs(j)%MOD)%MOD;
f[i][j+a-b+bas]=(f[i][j+a-b+bas]+f[i-1][j+bas])%MOD;
}
}
}
}
}
printf("%lld\n",g[n][bas]);
}
int main(){
int T=read();
while(T--)CFmode();
return 0;
}
标签:01,两个,题解,sum,Grandmaster,CF1615F,操作,LEGOndary
From: https://www.cnblogs.com/BigSmall-En/p/16661096.html