题解:P11487 「Cfz Round 5」Gnirts 10

· · 题解

挺好的题,来写题解了!

首先,我们可以想到,要对答案是 i01 序列 T 计数。不妨设这样的串的数量是 F(i),那么答案是 \sum\limits_{i=1}^niF(i)

Pre_iS 的长度为 i 的前缀。

考虑对 S 中长度为 i 的前缀检查一个 01T 是否合法。我们不难想到一个正确的贪心:设当前匹配到 s_i,设 s_iT_p 匹配,那么 s_{i+1} 匹配到 T_p 往后第一个等于 s_{i+1} 的位置,没有那么整个前缀就匹配失败了。

那么我们将 T 划成若干个由极长的 0/1 组成的连续段(伏笔 1),不难发现这时的子序列要么在某一段内一点不取,要么就取一段前缀(其实可以合并成一种情况)。

发现若 T 中有 Pre_i 做为前缀,那么其一定是在 Pre_i 中插入若干个 0/1 连续段的方式生成的。怎么不重不漏的插呢?不妨对于相邻的两个字符讨论。

$01$:只能插 $0$。因为是前缀嘛。 $10$:只能插 $1$,原因同上。 $11$:只能插 $0$,原因同上。 这个限制可以规约成:设相邻两个字符中右边的那个是 $x$,那么中间只能填 $1-x$。 不难发现这个限制能够以一种成功的方式归约到 $1$ 前面的部分与 $Pre_p$ 后面的部分。(这里假设我们在算 $F(p)$。) 原因很简单:对于 $1$ 前面的部分,肯定不能与之相同,正好符合我们的这个条件; 对于 $p$ 后面的部分,其肯定不能与 $p+1$ 相同,不然答案肯定是 $p+1$ 或者更多。$p=n+m$ 的情况另算。 那么我们设现在有 $C_0$ 个可以插 $0$ 的空隙,$C_1$ 个可以插 $1$ 的空隙,已经用掉 $S_0$ 个 $0$,$S_1$ 个 $1$ 了。 那么答案就是 $F(m-S_0,C_0) \times F(n-S_1,C_1)$。其中 $F(n,k)=\dbinom{n+k-1}{k-1}$,但是 $F(0,0) =1$。 直接预处理组合数然后 $O(n+m)$ 算出就好了。 对于 $p=n+m$ 的情况:考虑 $S$ 中是否有 $n$ 个 $1$ 与 $m$ 个 $0$,有的话就给总答案加上 $n+m$。 ~~不值得注意的是,这玩意在赛时卡了我快半个小时。~~ 就做完了。 ~~欢迎大家多多膜拜我的学长/IOIAKer @Sharpsmile,他以 0.01h 的优势拿下了本场比赛的 rk1。~~ ```cpp #include<bits/stdc++.h> #define int long long #define doub long double #define PII pair<int,int> #define mp make_pair #define fir first #define sec second #define PC __builtin_popcountll using namespace std; const int maxn=6e6+7; const int mod=2933256077; int n,m; bool Ss[maxn]; int Fac[maxn]; int iFac[maxn]; inline int qpow(int a,int b){ int ans=1; while(b){ if(b&1)ans=1ull*ans*a%mod; a=1ull*a*a%mod; b>>=1; } return ans; } inline void Init(){ Fac[0]=1; for(int i=1;i<=n+m+5;i++)Fac[i]=1ull*Fac[i-1]*i%mod; iFac[n+m]=qpow(Fac[n+m],mod-2); for(int i=n+m-1;i>=0;i--)iFac[i]=1ull*iFac[i+1]*(i+1)%mod; } inline int C(int n,int m){ if(n<0||m<0||n<m)return 0; return 1ull*Fac[n]*iFac[m]%mod*iFac[n-m]%mod; } inline int F(int n,int m){ if(n<0||m<0)return 0; if(n==0&&m==0)return 1; return C(n+m-1,m-1); } signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #ifndef ONLINE_JUDGE freopen("1.in","r",stdin); freopen("1.out","w",stdout); #endif cin>>n>>m; Init(); for(int i=1;i<=n+m;i++){ char t; cin>>t; Ss[i]=t-'0'; } int C0=0,C1=0; int S0=0,S1=0; int ans=0; for(int i=0;i<n+m;i++){ if(i){ if(Ss[i]==0)S0++; else S1++; } if(Ss[i+1]==0)C1++; else C0++; ans=(ans+1ull*i*F(m-S0,C0)%mod*F(n-S1,C1)%mod)%mod; } if(Ss[n+m]==0)S0++; if(Ss[n+m]==1)S1++; if((S0==m)&&(S1==n))ans+=(n+m); ans%=mod; cout<<ans<<endl; return 0; } ```