UESTC 电子科大专题训练 数论 G

UESTC 1718

题意:在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;
}

 

转载于:https://www.cnblogs.com/max88888888/p/7257990.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值