BZOJ 1951: [Sdoi2010]古代猪文 Lucas定理

Description

你的面前有N个数排成一行。分别为A1, A2, … , An。你打算在每相邻的两个 Ai和 Ai+1 间都插入一个加号或者
减号或者乘号。那么一共有 3^(n-1) 种可能的表达式。你对所有可能的表达式的值的和非常感兴趣。但这毕竟太
简单了,所以你还打算支持一个修改操作,可以修改某个Ai 的值。你能够编写一个程序对每个修改都输出修改完
之后所有可能表达式的和吗?注意,修改是永久的,也就是说每次修改都是在上一次修改的基础上进行, 而不是
在最初的表达式上进行。

Input

第一行包含 2 个正整数 N 和 Q,为数的个数和询问的个数。
接下来一行 n 个非负整数,依次表示a1,a2...an
在接下来 Q 行,其中第 ?? 行两个非负整数Ti 和Vi,表示要将 A ti 修改为 Vi。其中 1 ≤ Ti ≤ N。
保证对于 1 ≤ J ≤ N, 1 ≤ i≤ Q,都有 Aj,Vi ≤ 10^4。
N,Q<=100000,本题仅有三组数据

Output

输出共 Q 行,其中第 i 行表示第 i 个询问之后所有可能表达式的和,对10^9 + 7 取模。

Sample Input

5 5
9384 887 2778 6916 7794
2 8336
5 493
3 1422
1 28
4 60

Sample Output

890543652
252923708
942282590
228728040
608998099

Solution
题意是求G^sum(C(n,k)),由于999911659是质数,可将sum(C(n,k))模phi(999911659)
999911659=2*3*4679*35617,直接lucas定理处理,然后用中国剩余定理即可
最后注意有个坑,就是G为999911659,输出0

#include<bits/stdc++.h>
 
typedef unsigned char uchar;
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
 
#define xx first
#define yy second
 
template<typename T> inline T max(T a,T b){return a>b?a:b;}
template<typename T> inline T min(T a,T b){return a<b?a:b;}
template<typename T> inline T abs(T a){return a>0?a:-a;}
template<typename T> inline void repr(T &a,T b){if(a<b)a=b;}
template<typename T> inline void repl(T &a,T b){if(a>b)a=b;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
 
const int mod[4]={2,3,4679,35617},rmod=999911659;
 
int cc[4][36000],ny[4][36000],mny[4];
 
void exgcd(int a,int b,int &x,int &y)
{
    if(!b)
        x=1,y=0;
    else
    {
        exgcd(b,a%b,y,x);
        y-=(a/b)*x;
    }
}
 
inline int c(int n,int k,int m)
{
    if(n<k)return 0;
    return (ll)cc[m][n]*ny[m][k]*ny[m][n-k]%mod[m];
}
 
int lucas(int n,int k,int m)
{
    if(n<k||k<0)return 0;
    if(n<mod[m]&&k<mod[m])return c(n,k,m);
    return lucas(n/mod[m],k/mod[m],m)*c(n%mod[m],k%mod[m],m)%mod[m];
}
 
#define pmax 40000
int prime[10000],pm;
bool np[pmax+1];
inline void initprime()
{
    for(int i=2;i<=pmax;i++)
    {
        if(!np[i])prime[pm++]=i;
        for(int j=0;j<pm&&i*prime[j]<=pmax;j++)
        {
            np[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
 
int ys[30][2],yc,rn,cf=0;
 
inline void judge(int x)
{
    int s[4];
    for(int i=0;i<4;i++)
        s[i]=lucas(rn,x,i);
    ll ans=0;
    for(int i=0;i<4;i++)
        ans+=(ll)(rmod-1)/mod[i]*mny[i]*s[i];
    cf=(ans+cf)%(rmod-1);
}
 
void dfs(int x,int yi)
{
    if(yi==yc)
        judge(x);
    else
        for(int i=0,y=x;i<=ys[yi][1];i++,y*=ys[yi][0])
            dfs(y,yi+1);
}
 
int main()
{
    initprime();
    int tmp;
    for(int i=0;i<4;i++)
        exgcd(mod[i],(rmod-1)/mod[i]%mod[i],tmp,mny[i]);
    for(int i=0;i<4;i++)
    {
        cc[i][0]=ny[i][0]=1;
        for(int j=1;j<mod[i];j++)
        {
            cc[i][j]=(ll)cc[i][j-1]*j%mod[i];
            exgcd(mod[i],cc[i][j],tmp,ny[i][j]);
        }
    }
    int n,g;
    scanf("%d%d",&rn,&g);
    if(g==rmod)
    {
        puts("0");
        return 0;
    }
    n=rn;
    for(int i=0;i<pm;i++)
    {
        if(n%prime[i]==0)
        {
            ys[yc][0]=prime[i];
            while(n%prime[i]==0)n/=prime[i],ys[yc][1]++;
            yc++;
        }
    }
    if(n>1)ys[yc][0]=n,ys[yc][1]=1,yc++;
    dfs(1,0);
    if(cf<0)cf+=rmod-1;
    ll ans=1;
    for(ll u=g,i=1;i<=cf;i<<=1)
    {
        if(cf&i)ans=ans*u%rmod;
        u=u*u%rmod;
    }
    printf("%lld\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值