P5224 Candies

题目背景
JerryC有一大袋糖果,他正以1\ t/ms1 t/ms的速度食用着这一袋糖果…

题目描述
JerryC的糖果有NN箱(两两之间不同)。他一开始想挑MM箱出来,但是觉得吃起来不过瘾,所以又想要多拿一些出来。由于他比较喜欢数字KK,所以只要拿出来的糖的量x(x \ge M)x(x≥M)满足:x \equiv M\ (\bmod\ K)x≡M (mod K),JerryC就会得到满足感。

求有多少种方案使得JerryC得到满足感。请输出方案数\bmod\ 1004535809mod 1004535809的结果。

输入格式
一行三个非负整数NN,MM,KK。

输出格式
一行一个非负整数,表示方案数\bmod\ 1004535809mod 1004535809的结果。

输入输出样例
输入 #1复制
10 2 3
输出 #1复制
342
说明/提示
样例解释:

可以拿出来:2箱 5箱 8箱,组合数算一下就是了:

\binom{10}{2}+\binom{10}{5}+\binom{10}{8}=342( 210 )+( 510 )+( 810)=342数据范围:

测试点编号 N\leN≤ K\leK≤
11 11 11
2-32−3 10^610
6
1010
4-84−8 10^{12}10
12
100100
9-129−12 10^{15}10
15
10^310
3

12-2012−20 10^{18}10
18
10^410
4

0 \leq M < K
0≤M<K

其实这道题就是循环卷积,求的就是(1+x) nmod(x k−1)。
看完题后直接可以列出公式
(1+x) n=i=0∑n( in​)x i
这时候把指数模上k后所得结果相同的列在一起就是答案了.
本算法的时间复杂度 O(k\log k\log n)O(klogklogn)
特殊情况下(k为2的幂次方或其他特殊情况时)为 O(k\log k)O(klogk)

代码

#include<bits/stdc++.h>
#define ll long long
#define N 400007
#define reg register
#define p 1004535809
using namespace std;

struct poly{
    int a[N];
    int t;
};

int rev[N];
int inv;

inline int power(int a,int t){
    int res = 1;
    while(t){
        if(t&1) res = (ll)res*a%p;
        a = (ll)a*a%p;
        t >>= 1;
    }
    return res;
}

inline void NTT(poly &f,int type,int lim){
    for(reg int i=1;i<=lim;++i){
        if(i>=rev[i]) continue;
        swap(f.a[i],f.a[rev[i]]);
    }
    reg int w,r,x,y,rt;
    for(reg int mid=1;mid<lim;mid<<=1){
        r = mid<<1;
        rt = power(3,(p-1)/r);
        if(type==-1) rt = power(rt,p-2);
        for(reg int j=0;j<lim;j+=r){
            w = 1;
            for(reg int k=0;k<mid;++k){
                x = f.a[j+k];
                y = (ll)w*f.a[j+k+mid]%p;
                f.a[j+k] = (x+y)%p;
                f.a[j+k+mid] = (x-y+p)%p;
                w = (ll)w*rt%p;
            }
        }
    }
}

ll n;
int m,k,lim,l;
poly F;

poly power(poly f,ll n){
    poly g,h;
    g.t = f.t;
    memset(g.a,0,sizeof(g.a));
    g.a[0] = 1;
    while(n){
        if(n&1){
            h = f;
            NTT(h,1,lim),NTT(g,1,lim);
            for(reg int i=0;i<=lim;++i)
                g.a[i] = (ll)g.a[i]*h.a[i]%p;
            NTT(g,-1,lim);
            for(reg int i=0;i<=lim;++i)
                g.a[i] = (ll)g.a[i]*inv%p;
            for(reg int i=k;i<=lim;++i){
                g.a[i%k] = (g.a[i%k]+g.a[i])%p;    
                g.a[i] = 0;
            }
        } 
        n >>= 1;
        if(!n) break;
        NTT(f,1,lim);
        for(reg int i=0;i<=lim;++i)
            f.a[i] = (ll)f.a[i]*f.a[i]%p;
        NTT(f,-1,lim);
        for(reg int i=0;i<=lim;++i)
            f.a[i] = (ll)f.a[i]*inv%p;
        for(reg int i=k;i<=lim;++i){
            f.a[i%k] = (f.a[i%k]+f.a[i])%p;
            f.a[i] = 0;
        }    
    }
    return g;
}

signed main(){ 
    scanf("%lld%d%d",&n,&m,&k);
    lim = 1,l = -1;
    while(lim<=(k<<1)){
        lim <<= 1;
        ++l;
    }
    inv = power(lim,p-2);
    for(reg int i=1;i<=lim;++i) 
        rev[i] = (rev[i>>1]>>1)|((i&1)<<l);
    F.t = lim;
    F.a[0] = F.a[1] = 1;    
    F = power(F,n);
    printf("%d",F.a[m]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值