前记:
TM终于决定以后干啥了。这几天睡的有点多。困饿交加之间喝了好多水。可能是灌脑了。
切记两件事:
1.安心当单身狗
2.顺心码代码
题意:
给你N种颜色的珠子,串一串长度问N的项链,要求旋转之后重合的算是同一种项链。问一共有多少中可能。结果模p。
1 <= N <= 1000000000, 1 <= P <= 30000
思路:
首先是我们的POLYA定理,给定的公式是这样的sigma(N^gcd(N,i))/N i从0到N-1.
然后是优化的问题。因为如果我们枚举i累加一定会超时。
这道题考虑的是gcd(N,i)的种类是有限的。我们只要求出每种gcd有多少个就可以了。
考虑N=X*GCD I=Y*GCD
由此我们知道gcd(x,y)一定是1.
所以考虑枚举x,GCD的个数应该是x的欧拉。
坑点:
春困。TM变量总是写错。这道题的减一来源于提前把N给除掉了【这个N经常容易被人忽略】
#include<iostream> #include<math.h> #include<string.h> #include<stdio.h> using namespace std; int prime[100000];//0代表是素数 int tmp[100000];//素数的序列 int part[100000];//将t分解 int num[100000];//每个质因数的分解数量 int jilu[100000];//dfs记录某个质因数用了几个 long long ans,t,p;//t是N,p是p int partn;//记录一共分解成了几种素数 void fprime()//筛法打表 { int atmp=0,j; for(int i=2;i<100000;i++) { if(!prime[i]) { tmp[atmp++]=i; } for(j=0;j<atmp;j++) { if(i*tmp[j]>=100000) break; prime[i*tmp[j]]=1; if(i%tmp[j]==0) break; } } } void depart(int t){ int sq=sqrt(t)+1; for(int i=0;tmp[i]<=sq;i++){ if(t%tmp[i]==0){ part[partn]=tmp[i]; while(t%tmp[i]==0){ num[partn]++; t/=tmp[i]; } partn++; } } if(t>1){ part[partn]=t; num[partn]=1; partn++; } } void init(){ memset(num,0,sizeof(num)); partn=0; ans=0; } long long quick_pow(long long a,long long b){ long long rel=1; while(b){ if(b&1){ rel*=a; rel%=p; } a*=a; a%=p; b>>=1; } return rel; } long long oula(){ long long rel=1; for(int i=0;i<=partn;i++){ if(jilu[i]){ rel*=(part[i]-1)*quick_pow(part[i],jilu[i]-1); rel%=p; } } return rel; } void dfs(int pos,long long nnum){ if(pos>partn){ long long a=oula(); long long b=quick_pow(t,t/nnum-1); ans+=(a%p)*(b%p)%p; ans%=p; return; } long long ttt=1; for(int i=0;i<=num[pos];i++){ jilu[pos]=i; dfs(pos+1,nnum*ttt); ttt*=part[pos]; } } int main() { fprime(); int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%I64d%I64d",&t,&p); init(); depart(t); partn--; dfs(0,1); printf("%I64d ",ans); } }