其他分享
首页 > 其他分享> > CF1615F LEGOndary Grandmaster

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}\) 对答案的贡献。

转移分阶段,每个阶段有几个决策:

  1. 该位置原先就有 \(0\) 或 \(1\) 。

  2. 该位置为问号,可以选择填 \(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