珍珠项链——容斥的应用
题目链接:https://www.luogu.com.cn/problem/P2768
大致题意:
情人节之际,用K种珍珠为GF做一串珍珠垂饰。珍珠垂饰是由珍珠连接而成的,其长度就是珍珠垂饰上珍珠的个数。每种珍珠他都拥有N颗。根据将珍珠垂饰打开后珍珠不同的排列顺序可以区别不同种类的项链。现在询问可以组成多少种长度为1至N的不同的珍珠垂饰?其中每串珍珠垂饰都要必须由K种珍珠连成。
答案取模1234567891(质数)。
分析:
一般初遇题目总会想手推一下样例,样例的简单不影响我们发现题眼。2 1这组能帮我们确定题目的意思,让我们来看3 2那组。
8是怎么来的呢?用1,2代表两种珍珠:(1,2),(2,1),(1,1,2),(1,2,1),(2,1,1),(1,2,2),(2,1,2),(2,2,1)共8种。
暴力?别别别,来看看限制条件:每串都必须由K种珍珠连成。如果没有这个限制条件,那么kn就能求得长度为n,种类为k的全部情况。我们会有这样一种想法,把kn里不符合题目的减掉,不符合题目的明显是缺少一种或多种珍珠的情况,脑补一下,一个个圈交织在一起,转念一想,这不就是容斥的图析吗!是不是有种豁然开朗的感觉。
公式:
$$\sum_{i=0}^{k} (-1)^{i}C_{i}^{k} \sum_{j=1}^{n}(k-i)^{j}$$
其中(-1)i是容斥,后面的就是分析里所说的,因为kn里的n可以取多个值,所以要将1~n的情况都累加起来。
代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 using namespace std;
5 #define int long long
6 #define debug printf("zjyvegetable")
7 inline int read(){
8 int a=0,b=1;char c=getchar();
9 while(!isdigit(c)){if(c=='-')b=-1;c=getchar();}
10 while(isdigit(c)){a=a*10+c-'0';c=getchar();}
11 return a*b;
12 }
13 const int N=50,mo=1234567891;
14 int t,n,k,q[N];
15 int quick(int x,int z){
16 int ans=1;
17 while(z){
18 if(z&1)ans=(ans*x)%mo;
19 z>>=1;
20 x=(x*x)%mo;
21 }
22 return ans;
23 }
24 int C(int x,int y){
25 return ((q[y]*quick(q[x],mo-2))%mo)*quick(q[y-x],mo-2)%mo;
26 }
27 int cal(int x,int y){
28 if(x==1)return y;
29 return (quick(x,y+1)-x)%mo*quick(x-1,mo-2)%mo;
30 }
31 signed main(){
32 int ans,pr;
33 t=read();
34 q[0]=1;
35 for(int i=1;i<=35;i++){
36 q[i]=(q[i-1]*i)%mo;
37 }
38 while(t--){
39 ans=0;pr=1;
40 n=read();k=read();
41 for(int i=k;i>=1;i--){
42 ans=((ans+pr*C(k-i,k)*cal(i,n))%mo+mo)%mo;
43 pr=-pr;
44 }
45 printf("%lld\n",ans);
46 }
47 return 0;
48 }