CodeForces 1615F LEGOndary Grandmaster
我们先考虑如果没有 ?
,这个问题应该怎么做。
考虑到翻转两个相邻且相同的位置是困难的,但是我们如果吧所有奇数位置上的数翻转,就变成了交换两个相邻的数,这个东西显然是好做的。
这个东西有解就是两个串中的 1
个数相同,需要的次数就是把每个 1
的位置摘出来,然后对位的两个数的差的绝对值之和。
于是我们会做没有 ?
的情况了。
但是这个东西,显然不好在有 ?
的情况下统计,所以我们还需要转化。
对于没有 ?
的 \(s,t\),设 \(a_i\) 为 \(s\) 中前 \(i\) 个位置中 1
的个数,\(b_i\) 为 \(t\) 中前 \(i\) 个位置中 1
的个数,则我们的交换次数也可以表示成 \(\sum |a_i-b_i|\)。
于是我们直接 dp 即可,设 \(f_{i,j}\) 表示前 \(i\) 个位置 \(a_i-b_i=j\) 的方案数,\(g_{i,j}\) 相当于把整个串反转过来的 \(f_{i,j}\)。转移显然。
于是答案就是 \(\displaystyle\sum_{i=1}^{n-1}\sum_{j=-n}^{n}(f_{i,j}\times g_{i+1,-j}\times |j|)\)。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 2005
#define mod 1000000007
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
int T=1,n,f[N][N<<1],g[N][N<<1];
char s[N],t[N];
bool check(char a,int b){
if(a=='?')return 1;
return a-'0'==b;
}
void solve(int cs){
cin>>n>>s+1>>t+1;
for(int i=1;i<=n;i+=2){
if(s[i]=='1')s[i]='0';
else if(s[i]=='0')s[i]='1';
if(t[i]=='1')t[i]='0';
else if(t[i]=='0')t[i]='1';
}
for(int i=0;i<=n+1;i++){
for(int j=0;j<=n*2;j++){
f[i][j]=g[i][j]=0;
}
}
f[0][n]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=n*2;j++){
for(int u=0;u<2;u++){
for(int v=0;v<2;v++){
if(check(s[i+1],u)&&check(t[i+1],v)&&j+u-v>=0&&j+u-v<=n*2){
(f[i+1][j+u-v]+=f[i][j])%=mod;
}
}
}
}
}
g[n+1][n]=1;
for(int i=n+1;i>1;i--){
for(int j=0;j<=n*2;j++){
for(int u=0;u<2;u++){
for(int v=0;v<2;v++){
if(check(s[i-1],u)&&check(t[i-1],v)&&j+u-v>=0&&j+u-v<=n*2){
(g[i-1][j+u-v]+=g[i][j])%=mod;
}
}
}
}
}
int res=0;
for(int i=1;i<n;i++){
for(int j=-n;j<=n;j++){
(res+=f[i][n+j]*g[i+1][n-j]%mod*abs(j)%mod)%=mod;
}
}
cout<<res<<'\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}