网上看到一个挺妙的解法
题意
规定一个 (01) 串的价值为其最少需要的区间 (01) 翻转次数,使得其成为同色的串。
初始给定一个含有 (01) 与若干 (?) 字符的初始串 (S) ,把 (K) 个 (S) 收尾相接得到串 (T) 。求将 (T) 中每个 (?) 都换为 (0) 或 (1) 时,所有串的价值总和。答案对 (10^9+7) 取模。
分析
先不考虑 (?) 的限制。
考虑将串收尾相接,变成一个环。不难想到,环上的 (01) “脉冲”个数一定是偶数。且稍加观察可以得到,当“脉冲”数量为 (n) 时,最少需要的区间翻转次数就是 ({nover 2})。
由于等效于一个“脉冲”的贡献是 ({1over 2}),由于其总数为偶数,所以正确性没有影响。
当 (K) 个串收尾相连时,只需要算出第一个串的“脉冲”贡献,然后直接乘 (K) 即可。
考虑 (?) 的限制。
当出现 (?) 时,不妨记为 (s_i=?) 。((s_0=s_nwedge s_{n+1}=s_1))
则从概率角度,(s_i) 有 ({1over 2}) 的概率是 (0),有 ({1over 2}) 的概率是 (1) 。
当 (s_{i-1} eq ?) 时,统计量中,一半的 (s_{i-1}s_i) 贡献“有脉冲”,为 ({1over 2}) ;一半的 (s_{i-1}s_i) 贡献“无脉冲”,为 (0) 。
等效于一个 (s_{i-1}s_i) 的贡献为 ({1over 4}) ,同样由于出现 (?) 时,总数为偶数,所以正确性没有影响。
同理可得,(s_{i+1} eq ?) 时一个 (s_is_{i+1}) 的贡献也为 ({1over 4}) 。
而出现 (s_is_{i+1}=??) 时,从概率角度,(00, 01, 10, 11) 的出现概率都是 ({1over 4});因此有无脉冲的概率仍均是 ({1over 2}) ,贡献同样也为 ({1over 4})。
所以同样当 (K) 个串相连时,只需算出第一个串的“脉冲”贡献和 (?) 贡献,然后直接乘 (Kcdot 2^{Kq}) 算出答案。
代码
记得特判 (K=1, |S|=1) 的情况即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<ll, ll> pii;
const int MOD=1e9+7;
inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%MOD) if(x&1) ans=ans*a%MOD; return ans; }
inline ll sumq(const string &s) { ll ans=0; for(auto c : s) ans+=(c=='?'); return ans; }
inline ll sumf(const string &s){
ll ans=0;
char lstc=s.back();
for(int i=0;i<s.size();lstc=s[i], ++i)
if(lstc=='?'||s[i]=='?') ++ans;
else if(lstc!=s[i]) ans+=2;
return ans*fpow(4, MOD-2)%MOD;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
string s;
ll k;
cin>>s>>k;
cout<<fpow(2, k*sumq(s))*k%MOD*sumf(s)%MOD*(s.size()>1||k>1);
cout.flush();
return 0;
}
感想
第一次见识到这种用概率和期望反过来求统计的方式,非常的 amazing 啊!
说起来,莫名其妙想到量子计算的纠缠态