bzoj4330:JSOI2012 爱之项链

时间:2021-08-27 02:53:47

题目大意:一串项链由n个戒指组成,对于每个戒指,一共有M个点,R种颜色,且旋转后相同的戒指是相同的,然后一串项链又由N个戒指组成,同时要满足相邻的两个戒指不能相同,这串项链上某个位置插入了一个特殊的东西,且如果特殊的东西插入的地方不同,即使戒指都是相同的,这两串项链也是不同的,求一共有多少不同的爱之项链。

思路:首先可以求出一共有多少种不同的戒指,又由于有那个特殊的东西,相当于这串项链即使旋转后相同,但特殊的东西插入的位置也肯定是不同的,因此即不考虑旋转,只考虑相邻位置不同的爱之项链的方案数。

令ans表示有多少种不同的戒指。

然后这样就可以运用容斥原理。对于第i个戒指和第i+1个戒指相同,可以看成第i个限制,然后第n个限制即第n个戒指和第1个限制不同,那么即要求满足所有限制的方案数。令Si为满足第i个限制的方案数的集合,那么即求S1~Sn的交,也就是其补集的并,那么就是总方案数减去所有不满足任意一个限制数加上所有不满足任意二个限制数......

然后这个式子是怎样的呢,首先总方案数显然就是ans^n,然后不满足任意一个限制数即有两个相邻的相同,可以看成一条边连接的两个点相同,就是C(n,1)*ans^(n-1),以此类推,然后可以得到:sigma(0<=i<=n,(-1)^i*C(n,i)*ans^(n-i)),然后发现这就是二项式定理,就可以得到(ans-1)^n, 但真的就是这样吗?当i=n时,就会有一个Bug,用公式算得的答案是(-1)^n,然而n个限制均不满足时的情况即所有颜色都一样,有ans种,因此还要加上(n&1?1-m:m-1)才行。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define p 3214567
#define maxn 200020 int m,r,tot;
int prime[maxn],phi[maxn];
bool isprime[maxn];
long long n; int power(int a,long long k){
if (k==) return ;
if (k==) return a%p;
int x=power(a,k/),ans=1ll*x*x%p;
if (k&) ans=1ll*a*ans%p;
return ans;
} int fphi(int x){
int ans=x;
for (int i=;i*i<=x;i++)
if (x%i==){
ans=ans-ans/i;
while (x%i==) x/=i;
}
if (x!=) ans=ans-ans/x;
return ans;
} int main(){
scanf("%lld%d%d",&n,&m,&r);tot=,memset(isprime,,sizeof(isprime)),phi[]=;
for (int i=;i<maxn;i++){
if (isprime[i]) prime[++tot]=i,phi[i]=i-;
for (int j=;j<=tot && i*prime[j]<maxn;j++){
isprime[i*prime[j]]=;
if (i%prime[j]==){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-);
}
}
int ans=;
for (int i=;i*i<=m;i++)
if (m%i==){
ans=(ans+1ll*power(r,i)*fphi(m/i)%p)%p;
if (i*i!=m) ans=(ans+1ll*power(r,m/i)*phi[i]%p)%p;
}
ans=1LL*ans*power(m,p-)%p;
int t=(1ll*power(ans-,n)+(n&?-ans:ans-))%p;
printf("%d\n",(t+p)%p);
return ;
}