[ABC200F] Minflip Summation
前言
太妙啦!
本文只讲做法,不讲如何想到这个做法,没见过这个套路真的很难想啊。
题目
讲解
part1 01
我们先考虑没有问号的情况。
我们将所有数变为与第一个数相同的方法一定不劣。那么我们可以想到这样一个统计答案的方法,如果相邻两个数(按环考虑)不同,那么对答案的贡献为 \(\frac{1}{2}\)。用 \(C++\) 表示即:
\[\frac{\overset{|S|}{\underset{i=1}{\sum}}S_i==S_{i\%|S|+1} ? 0 : 1}{2}
\]
反复 \(K\) 次只需要将答案乘 \(K\) 即可。
part2 ?
考虑扩展版本。
我们发现每个 \(?\) 成为 \(0\) 和 \(1\) 的概率相同,而且彼此独立,所以我们可以求出期望再求出答案。
此时的答案为:
\[K\cdot \frac{\overset{|S|}{\underset{i=1}{\sum}}f(i,i\%|S|+1)}{2}\cdot 2^{cnt_?K}
\]
其中的函数 \(f(x,y)\) 表示 \(S_x,S_y\) 对答案产生的贡献。
- \(S_x\) 与 \(S_y\) 其中一个是问号,那么显然对答案的贡献为 \(\frac{1}{2}\)。
- 否则相同贡献 \(0\),不同贡献 \(1\)。
代码
int qpow(int x,int y)
{
int ret = 1;
while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
return ret;
}
int Add(int x,int y)
{
x += y; if(x >= MOD) x -= MOD;
return x;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%s",a+1);
n = strlen(a+1); k = Read();
for(int i = 1;i <= n;++ i)
{
int j = i+1 > n ? 1 : i+1;
if(a[i] == '?')
{
cnt++;
ans = Add(ans,inv2);
}
else if(a[j] == '?') ans = Add(ans,inv2);
else if(a[i] != a[j]) ans = Add(ans,1);
}
ans = 1ll * ans * k % MOD * inv2 % MOD * qpow(2,1ll * cnt * k % (MOD-1)) % MOD;
Put(ans);
return 0;
}