[jzoj3704]自古枪兵幸运E

Description

有n+m种物品,前n种物品的大小为1,后m种物品的大小为2,每种物品都有无限个
求用着n+m种物品放满大小为k的背包的方案数,答案对p取模
n,m<=1e5,k<=1e12,p<=1e6,保证p为质数

Solution

题解被samjia打爆了=w=
答案的生成函数显然就是

( 1 1 x ) n ( 1 1 x 2 ) n + m

题解给出了一个式子:
1 ( 1 x ) a ( 1 + x ) b = i = 1 a 1 ( 1 + x ) i C a + b i 1 a i 1 2 a + b i + i = 1 b 1 ( 1 x ) i C a + b i 1 b i 1 2 a + b i

有了这条式子很好做,不过其实原式直接可以化成
( 1 + x ) n ( 1 1 x 2 ) n + m

都是O(n+m)的,不过那个式子可能有用记一下问题不大

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

const int N=1e6+5;

int n,m,p,ty,fact[N],inv[N];
ll k;

int pwr(int x,int y) {
    int z=1;
    for(;y;y>>=1,x=(ll)x*x%p)
        if (y&1) z=(ll)z*x%p;
    return z;
}

int C(ll m,ll n) {
    if (m<n) return 0;
    if (m<p&&n<p) return (ll)fact[m]*inv[n]%p*inv[m-n]%p;
    return (ll)C(m/p,n/p)*C(m%p,n%p)%p;
}

int main() {
    freopen("luckye.in","r",stdin);
    freopen("luckye.out","w",stdout);
    for(scanf("%d",&ty);ty;ty--) {
        scanf("%d%d%lld%d",&n,&m,&k,&p);
        fact[0]=1;fo(i,1,p-1) fact[i]=(ll)fact[i-1]*i%p;
        inv[p-1]=pwr(fact[p-1],p-2);fd(i,p-2,0) inv[i]=(ll)inv[i+1]*(i+1)%p;
        int ans=0;
        fo(i,0,n) if (!((k-i)&1)) (ans+=(ll)C(n,i)*C((k-i)/2+n+m-1,n+m-1)%p)%=p;
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/alan_cty/article/details/80436680