[ABC200E] Minflip Summation

一、题目

题目描述

一个字符串 (T) 是由一个包含 (0,1,?) 的字符串 (S) 循环连接 (k) 次获得的。

字符串 (T) 中的每个 (?) 都可以换成 (0)(1),设得到的串是 (T'),求:

[sum F(T') ]

其中 (F(T')) 定义为把字符串通过区间异或 (1) 的方式变成相同的最小操作数。

(1leq |S|leq 10^5,1leq kleq 10^9)

解法

别被题目吓到了,其实是个水题。

首先想一想怎么才能做一个没有问号的序列 (S),可以把 (S) 差分,即得到数组 (s_1oplus s_2....s_{n}oplus s_1),不难发现这样做一定会有偶数个 (1),而且每次通过区间操作可以消掉一对 (1),并且 (S) 复制 (k) 次的差分数组就是 (S) 的差分数组复制 (k) 次获得的,不难写出答案:

[k imes frac{sum_{i=1}^n[s_i=s_{i\%n+1}]}{2} ]

然后再把问号考虑进来,可以先求出期望再乘以总方案数,只考虑 ([s_i=s_{i\%n+1}]) 的期望是好算的:

  • 如果 (s_i=?or s_{i\% n+1}=?),不难发现期望是 (50\%)
  • 否则期望是 ([s_i=s_{i\%n+1}])

但是注意 (n=k=1) 并且字符是 (?) 的时候会出问题(期望算错了),需要特判。

#include <cstdio>
#include <cstring>
const int M = 100005;
const int MOD = 1e9+7;
const int i2 = (MOD+1)/2;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,ans,t=1;char s[M];
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
signed main()
{
	scanf("%s",s+1);k=read();
	n=strlen(s+1);
	if(n*k==1) {puts("0");return 0;}
	for(int i=1;i<=n;i++)
	{
		int j=i%n+1;
		if(s[i]=='?') t=t*2%MOD;
		if(s[i]=='?' || s[j]=='?') ans=(ans+i2)%MOD;
		else if(s[i]!=s[j]) ans++;
	}
	ans=ans*k%MOD*i2%MOD*qkpow(t,k)%MOD;
	printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/C202044zxy/p/14761606.html