UESTC 电子科大专题训练 数论 G
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UESTC 电子科大专题训练 数论 G相关的知识,希望对你有一定的参考价值。
题意:在01串中选出长度为偶数,并且前一半是0,后一半是1的子序列方案数
思路:组合数+范德蒙恒等式 记录每个数前面0的个数pi和后面1的个数nexi(包括本身)遍历到第k个数的时候,如果是0 那么方案数为(因为计算第i位时,前面计算的i-1个答案都不包含这一位,但是第i位计算的答案都包含第i位 所以没有重复)
for(i=0, -> i=min(pi-1,nexi)) C(pi-1,i)*C(nexi,i+1) 可以由 C(l-1,i)=C(l,i+1)-C(l-1,i+1)推得 for(i=0; i<=min(pi,nexi)) C(l,i)*C(r,i) - for(i=0; i<=min(pi-1,nexi)) C(pi-1,i)*C(nexi,i) (∑求和不好写,我就直接for循环表示了)
再由范德蒙恒等式可推得 for(i=0; i<=m) C(m,i)*C(n,i)=C(m+n,m) (其中m<=n) 所以预处理出2e5以内的阶乘和逆元就可以O(n)*O(1)求解了
AC代码:
#include "iostream" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set" #include "map" #include "algorithm" #include "stdio.h" #include "math.h" #define ll long long #define bug(x) cout<<x<<" "<<"UUUUU"<<endl; #define mem(a) memset(a,0,sizeof(a)) #define mp(x,y) make_pair(x,y) #define pb(x) push_back(x) using namespace std; const long long INF = 1e18+1LL; const int inf = 1e9+1e8; const int N=1e5+100; const ll mod=1e9+7; ll fac[N<<1], inv[N<<1]; ll fpow(ll a, int b) {ll ans=1; for(;b;a=a*a%mod,b>>=1)if(b&1)ans=ans*a%mod; return ans;} ll Inv(ll x) {return fpow(x, mod-2);} ll C(int a, int b){ if(b==0 || a==b) return 1; return fac[a]*inv[b]%mod*inv[a-b]%mod; } void Init(){ //求组合数预处理优化,inv[i]表示i的阶乘的逆元 fac[0]=1; for(int i=1; i<(N<<1); ++i) fac[i]=fac[i-1]*i%mod, inv[i]=Inv(fac[i])%mod; } char s[N<<1]; int p[N<<1],nex[N<<1]; ll check(int t){ ll ret=0; int m=min(p[t],nex[t]); int mm=min(p[t]-1,nex[t]); ret=(ret+C(p[t]+nex[t],m))%mod; ret=(ret-C(p[t]-1+nex[t],mm))%mod; return ret; } int main(){ //ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); Init(); scanf("%s",s+1); int ls=strlen(s+1),t=0; for(int i=1; i<=ls; ++i){ if(s[i]==‘0‘) t++; p[i]=t; } t=0; for(int i=ls; i>=1; --i){ if(s[i]==‘1‘) t++; nex[i]=t; } ll ans=0; for(int i=1; i<=ls; ++i){ if(s[i]==‘0‘){ ans=(ans+check(i))%mod; } } ans=(ans+mod)%mod; printf("%lld\n",ans); return 0; }
以上是关于UESTC 电子科大专题训练 数论 G的主要内容,如果未能解决你的问题,请参考以下文章