ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1615F LEGOndary Grandmaster

2022-07-31 18:36:17  阅读:128  来源: 互联网

标签:方案 return 原题 ll Grandmaster CF1615F 答案 操作 LEGOndary


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有