[国家集训队] 礼物

洛谷 P2183 传送门

bzoj 2142 传送门

一共n个礼物,分给m个人,每个人分wi个。

如果不够分,当然是Impossible。

如果够分,考虑求出1-n的所有排列,然后前w1个分给第一个人,w2个分给第二个人......

剩下的那些,当做分给了第m+1个人。

这样共有 n! 种排列。

但是每个人(包括第m+1个)分到的礼物只要本质一样就行,排列顺序无所谓。

所以我们再除掉 w1!、w2!、...、wm+1! 就行了。

取模数为非质数,用扩展卢卡斯的方法计算阶乘即可。

 最开始全WA,调了一下发现是逆元求错了......求x逆元的函数会return x......

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 
 7 int n,m;
 8 ll p;
 9 ll w[8];
10 ll ans;
11 
12 ll ksm(ll bs,ll tp,ll mod)
13 {
14     ll ret=1;
15     while(tp)
16     {
17         if(tp&1)ret=ret*bs%mod;
18         bs=bs*bs%mod;
19         tp>>=1;
20     }
21     return ret;
22 }
23 
24 ll exgcd(ll a,ll b,ll &x,ll &y)
25 {
26     if(!b)
27     {
28         x=1;y=0;
29         return a;
30     }
31     ll ret=exgcd(b,a%b,y,x);
32     y-=a/b*x;
33     return ret;
34 }
35 
36 ll inv(ll x,ll mod)
37 {
38     ll ret,tmp;
39     exgcd(x,mod,ret,tmp);
40     return (ret%mod+mod)%mod;
41 }
42 
43 ll crt(ll a,ll pk)
44 {
45     return a*(p/pk)%p*inv(p/pk,pk)%p;
46 }
47 
48 ll fac(ll x,ll pi,ll pk)
49 {
50     if(!x)return 1;
51     ll ret=1;
52     for(ll i=2;i<=pk;i++)
53         if(i%pi)ret=ret*i%pk;
54     ret=ksm(ret,x/pk,pk);
55     for(ll i=2;i<=x%pk;i++)
56         if(i%pi)ret=ret*i%pk;
57     return ret*fac(x/pi,pi,pk)%pk;
58 }
59 
60 void cal(ll pi,ll pk)
61 {
62     ll cnt=0;
63     ll up=fac(n,pi,pk);
64     for(ll i=n;i;i/=pi)cnt+=i/pi;
65     ll down=1;
66     for(int j=1;j<=m;j++)
67     {
68         down=down*fac(w[j],pi,pk)%pk;
69         for(ll i=w[j];i;i/=pi)cnt-=i/pi;
70     }
71     ll tmp=up*inv(down,pk)%pk*ksm(pi,cnt,pk)%pk;
72     ans=(ans+crt(tmp,pk))%p;
73 }
74 
75 int main()
76 {
77     scanf("%lld",&p);
78     scanf("%d%d",&n,&m);
79     w[m+1]=(ll)n;
80     for(int i=1;i<=m;i++)scanf("%lld",&w[i]),w[m+1]-=w[i];
81     m++;
82     if(w[m]<0)return printf("Impossible"),0;
83     ll tp=p;
84     for(ll i=2;i*i<=p;i++)
85     {
86         if(tp%i)continue;
87         ll pk=1;
88         while(tp%i==0)tp/=i,pk*=i;
89         cal(i,pk);
90     }
91     if(tp>1)cal(tp,tp);
92     printf("%lld",ans);
93     return 0;
94 }

猜你喜欢

转载自www.cnblogs.com/eternhope/p/9903222.html