【bzoj2142】礼物

2015.03.25 19:23 Wed | 6次阅读 | 旧日oi | 固定链接 | 源码

Description

一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。小E从商店中购买了n件礼物,打算送给m个人,其中送给第i个人礼物数量为wi。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模P后的结果。

Input

输入的第一行包含一个正整数P,表示模;第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。

Output

若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。

Sample Input

100 4 2 1 2

Sample Output

12
【样例说明】
下面是对样例1的说明。
以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:
1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23
【数据规模和约定】
设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
对于100%的数据,1≤n≤109,1≤m≤5,1≤pi^ci≤10^5。

题解

啊啊啊啊啊!!!!!见过的数论第一神题……
质因数分解,exgcd,乘法逆元,中国剩余定理,快速阶乘,快速幂(这算?)
数据很水,之前tle是因为w数组最后一个写成m-sum了,然后我把快速阶乘那里优化了一个log吧大概,结果a了之后发现改不改只差8ms……
还有两发re,因为交错题了。

我的程序

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<bitset>
#include<stack>
#include<ctime>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define maxn 100010
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll p,m,n,cnt,ans;
ll ret[maxn]={1};
ll w[150];
struct Prime{
    ll val;
    ll num;
    ll mod;
}prime[maxn];
ll quick_pow(ll a,ll k,ll p=inf)
{
    ll ret=1;
    while(k)
    {
        if(k&1) ret=(ret*a)%p;
        a=(a*a)%p;k>>=1;
    }
    return ret;
}
void div(ll num)
{
    for(int i=2;i*i<=num;i++)
    {
        if(num%i==0)
        {
            prime[++cnt].val=i;prime[cnt].mod=1;
            while(num%i==0) {
                num/=i,prime[cnt].num++;
                prime[cnt].mod*=i;
            }
        }
    }
    if(num>1) prime[++cnt].val=prime[cnt].mod=num,prime[cnt].num=1; 
}
void exgcd(ll a,ll b,ll &x,ll &y,ll &d)
{
    if(!b) {
        x=1;y=0;d=a;
        return;
    }
    exgcd(b,a%b,y,x,d);y=y-a/b*x;
}
ll get_niyuan(ll a,ll b)
{
    ll x,y,d;
    exgcd(a,b,x,y,d);b/=d;
    return (x%b+b)%b;
}
ll solve(ll a,ll b,ll p)
{
    return a*get_niyuan(b,p)%p;
}
ll quick_jc(ll a,ll p,ll pa,ll &d)
{
    if(!a) return 1;d+=a/p;
    return quick_pow(ret[pa],a/pa,pa)*ret[a%pa]%pa*quick_jc(a/p,p,pa,d)%pa;
}
void work(int k)
{
    for(int i=1;i<=prime[k].mod;i++) 
    if(i%prime[k].val) ret[i]=ret[i-1]*i%prime[k].mod;
    else ret[i]=ret[i-1];
    ll c=0,a=quick_jc(n,prime[k].val,prime[k].mod,c),b=1,tmp=0;
    for(int i=1;i<=m;i++) 
    b=b*quick_jc(w[i],prime[k].val,prime[k].mod,tmp)%prime[k].mod;c-=tmp;
    ll t1=solve(a,b,prime[k].mod)*quick_pow(prime[k].val,c,prime[k].mod)%p;
    ll t2=get_niyuan(p/prime[k].mod,prime[k].mod)*(p/prime[k].mod)%p;
    ans=(ans+t1*t2)%p;
}
int main()
{
    p=read();n=read();m=read();
    int sum=0;
    for(int i=1;i<=m;i++) w[i]=read(),sum+=w[i];
    if(sum<n) w[++m]=n-sum;
    if(sum>n)
    {
        puts("Impossible");
        return 0;
    }
    div(p);
    for(int i=1;i<=cnt;i++) 
    work(i);
    printf("%lld\n",ans);
}```