Codeforces #441 Div1.D:(扩展Lucas)

传送门

题意:
给定 n ,p(n1e5,q2e9),l,r,求所有长度为n的序列,满足每一位可以是A,B,C,且任意前缀A的和大于B的和,且总的 AB[l,r]

题解:
http://codeforces.com/blog/entry/56135

这个转化太妙了,大概是类似找代表元的思想。

之后推导后可以用扩展Lucas求组合数了,好久没有打很不熟练啊。。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
} 
typedef pair<int,int> pii;
const int Maxn=1e5+50;
vector<pii>fac;
int n,p,l,r,vis[Maxn][35],v[35],fac2[Maxn][35];
pii fac1[Maxn][35];
long long ans;
inline int power(int a,int b,int mod){
    int rs=1;
    for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)rs=1ll*rs*a%mod;
    return rs;
} 
inline int calc_inv(int A,int id);
inline pii calc_fac(int A,int id){
    if(!A)return make_pair(0,1);
    if(vis[A][id])return fac1[A][id];
    vis[A][id]=1;int bl=A/fac[id].first;
    int t1=fac2[bl*fac[id].first][id];
    int t2=1ll*fac2[A][id]*calc_inv(fac2[bl*fac[id].first][id],id)%v[id];
    t1=1ll*t1*t2%v[id];
    pii t3=calc_fac(bl,id);
    return fac1[A][id]=make_pair(t3.first+bl,1ll*t1*t3.second%v[id]);
} 
inline void exgcd(int A,int B,long long &x,long long &y){
    if(!B){
        x=1;y=0;return;
    }
    exgcd(B,A%B,x,y);
    long long t=x;
    x=y;
    y=t-A/B*y;
}
inline int calc_inv(int A,int id){
    long long x,y;
    exgcd(A,v[id],x,y);
    x%=v[id];x+=v[id];x%=v[id];
    return x;
}
inline void merge(int &A,int &B,int C,int D){
    long long k1,k2;
    exgcd(A,C,k1,k2);
    k1%=C;k1+=C;k1%=C;
    k1=k1*(D-B);
    k1%=C;k1+=C;k1%=C;
    B=(k1*A+B);
    A=A*C;B%=A; 
} 
inline int calc(int A,int B){
    if(B<0)return 0;
    if(!B)return 1;
    int now=1,rs=0;
    for(int i=0;i<fac.size();++i){
        pii t1=calc_fac(A,i),t2=calc_fac(B,i),t3=calc_fac(A-B,i); 
        t1.first-=(t2.first+t3.first);
        t1.second=1ll*t1.second*calc_inv(t2.second,i)%v[i]*calc_inv(t3.second,i)%v[i];
        for(int j=1;j<=t1.first;j++)t1.second=1ll*fac[i].first*t1.second%v[i];
        merge(now,rs,v[i],t1.second);
    }
    return rs;
}

int main(){
    n=rd(),p=rd(),l=rd(),r=rd();int tt=p;
    for(int i=2;i*i<=tt;++i){
        if(!(tt%i)){
            pii t=make_pair(i,0);v[fac.size()]=1;
            while(!(tt%i))tt/=i,++t.second,v[fac.size()]*=i;
            fac.push_back(t);
        }
    }
    if(tt!=1)v[fac.size()]=tt,fac.push_back(make_pair(tt,1));
    for(int i=0;i<fac.size();i++){
        fac2[0][i]=1;
        for(int j=1;j<=n;j++){
            fac2[j][i]=1ll*fac2[j-1][i]*(j%fac[i].first?j:1)%v[i]; 
        }
    }
    for(int C=0;C<=n;++C){
        r=min(r,n-C);if(r<l)break;
        int rs=n-C,tp=calc(n,C);
        int L=l+((l&1)!=(rs&1)),R=r-((r&1)!=(rs&1)); 
        if(L>R)continue;
        ans+=calc(rs,(rs-L)/2)*1ll*tp%p;
        ans-=calc(rs,(rs-R-2)/2)*1ll*tp%p;
        ans%=p;ans+=p;ans%=p; 
    }
    cout<<ans<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值