CF1615F LEGOndary Grandmaster
作者:互联网
written on 2022-05-05
第一次看到这类题目,显然丝毫没有下手之处。但其实这是一道套路题,这道题就用来总结经验好了。
原题操作:把相邻两个 \(0\) 变成 \(1\) 或把相邻两个 \(1\) 变成 \(0\) 。定义 \(s\) 到 \(t\) 的距离为 最少操作次数 使得 \(s\) 变成 \(t\) ,如过没法变则距离为 \(0\) 。
转化操作:这样的操作等价于对串的每一个奇数位(偶数位)取反,一次操作即为一次交换操作。
所以我们可以先考虑原题的一个子问题,即:求对于转化后的两个串 \(s'\),\(t'\) ,要求最少的交换次数使得两串相同。
对于这个子问题,直观的解决思路是找到两串中每一个 \(1\) 出现的位置 \(A_i\), \(B_i\) ,答案即为 \(∑A_i-B_i\) 。(\(1\) 的数目不同即不合法,答案为 \(0\))
这个思路可以进一步转化,考虑前缀和 \(P_i\),\(Q_i\),答案亦为 \(∑P_i-Q_i\) 。
这时我们就有了这个大问题的子问题的求解方案。原题问题在这个子问题的基础上多了不确定的问号位置。这时可以用dp求解,我们定义 \(f_{i,j}\) 表示串处理到第 \(i\) 个位置,当前 \(P_i-Q_i=j\) 时的操作方案数,最后对答案的统计我们用贡献法,用方案数求出每一个 \({i,j}\) 对答案的贡献。
转移分阶段,每个阶段有几个决策:
-
该位置原先就有 \(0\) 或 \(1\) 。
-
该位置为问号,可以选择填 \(0\) 或 \(1\) 。
转移方程也不难,写在代码里了。
我们发现对于一个位置 \(i\) ,这是前缀统计的方案数,我们还需要统计后面的方案数,于是从后往前再扫一遍,维护方案数 \(g\) 。
至于对答案贡献的计算方法,想想我们刚刚解决的那个子问题。一个 \({i,j}\) 对应一种方案,它的答案只与 \(j\) 有关,也就是它要变成0的次数,也就是 \(|j|\) 。所以统计的时候,用方案数乘以贡献,求和即是答案。
Code
#include<bits/stdc++.h>
using namespace std;
char change(char x)
{
if(x=='1') return '0';
if(x=='0') return '1';
return x;
}
#define N 2005
typedef long long ll;
const ll mod=1e9+7;
int n;
char a[N],b[N];
ll f[N][N<<1],g[N][N<<1];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%s%s",&n,a+1,b+1);
for(int i=1;i<=n;i+=2) a[i]=change(a[i]),b[i]=change(b[i]);
f[0][N]=1;
for(int i=1;i<=n;i++)
{
for(int lst=-(i-1);lst<=i-1;lst++)
{
for(int k=0;k<=1;k++)
{
if(isdigit(a[i])&&a[i]!=k+'0') continue;
for(int l=0;l<=1;l++)
{
if(isdigit(b[i])&&b[i]!=l+'0') continue;
int now=lst+k-l;
f[i][now+N]=(f[i][now+N]+f[i-1][lst+N])%mod;
}
}
}
}
g[n+1][N]=1;
for(int i=n;i;i--)
{
for(int lst=-(n-i);lst<=n-i;lst++)
{
for(int k=0;k<=1;k++)
{
if(isdigit(a[i])&&a[i]!=k+'0') continue;
for(int l=0;l<=1;l++)
{
if(isdigit(b[i])&&b[i]!=l+'0') continue;
int now=lst+k-l;
g[i][now+N]=(g[i][now+N]+g[i+1][lst+N])%mod;
}
}
}
}
ll ans=0;
for(int i=1;i<=n;i++) for(int now=-i;now<=i;now++) ans=(ans+(f[i][now+N]*g[i+1][-now+N])%mod*abs(now)%mod)%mod;
printf("%lld\n",ans);
for(int i=0;i<=n+1;i++) for(int now=-n;now<=n;now++) f[i][now+N]=g[i][now+N]=0;
}
}
注意这题卡常,所以清空不能用 \(memset\) 。
标签:方案,return,原题,ll,Grandmaster,CF1615F,答案,操作,LEGOndary 来源: https://www.cnblogs.com/Freshair-qprt/p/16537749.html