POJ burnside&&polya整理练习
POJ 2409 Let it Bead
这题就是polya公式的直接套用,唯一麻烦的是置换群的种类数,由于可以翻转,所以除了要加上pow(c,gcd(s,i))这些平面旋转的置换群,还要加上翻转的。由于翻转的情况奇偶是不同的,所以需要分开讨论:偶数:pow(c,(s-2)/2+2)*(s/2)+pow(c,(s/2))*(s/2);(里面包含了两个对点和两个对边的旋转) 奇数:pow(c,(s-1)/2+1)*s;(一个点和对边的旋转)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<set> #include<vector> #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(a,b,i) for(i=a;i<=b;++i) #define For(a,b,i) for(i=a;i<b;++i) using namespace std; inline void RD(int &ret) { char c; do { c=getchar(); } while(c<'0'||c>'9'); ret=c-'0'; while((c=getchar())>='0'&&c<='9') { ret=ret*10+(c-'0'); } } inline void OT(int a) { if(a>=10) { OT(a/10); } putchar(a%10+'0'); } int gcd(int a,int b) { if(b==0) { return a; } else { return gcd(b,a%b); } } int pow(int x,int y) { int i,j=1; for(i=0;i<y;++i) { j*=x; } return j; } int main() { int c,s,i,j,ans,sum; while(1) { RD(c); RD(s); if(c==0&&s==0) { break; } sum=0; for(i=1;i<=s;++i) { sum+=pow(c,gcd(i,s));//通用做法,而且数据量很小。 } if(s%2==0)//注意题意,这题的图案是可以翻转的,但并不是所有题目都这样,注意观察 { sum+=pow(c,(s-2)/2+2)*(s/2)+pow(c,(s/2))*(s/2); } else { sum+=pow(c,(s-1)/2+1)*s; } ans=sum/(2*s); printf("%d\n",ans); } return 0; }
POJ 1286 Necklace of Beads
典型的买一送一题,和上题一样,都是套用公式题目,这题和上题相比,还少了可以翻转的条件,而且颜色数量固定为3,所以就不过多赘述了。但要注意N=0时要特判一下,输出0。而且数据范围比之前那题大,要使用long long。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<set> #include<vector> #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(a,b,i) for(i=a;i<=b;++i) #define For(a,b,i) for(i=a;i<b;++i) using namespace std; inline void RD(int &ret) { char c; do { c=getchar(); } while(c<'0'||c>'9'); ret=c-'0'; while((c=getchar())>='0'&&c<='9') { ret=ret*10+(c-'0'); } } inline void OT(int a) { if(a>=10) { OT(a/10); } putchar(a%10+'0'); } int gcd(int a,int b) { if(b==0) { return a; } else { return gcd(b,a%b); } } long long pow(int x,int y)//注意数据范围,3的18次方就超了 { int i; long long j=1; for(i=0; i<y; ++i) { j*=x; } return j; } int main() { int s,i; long long ans,sum; while(1) { scanf("%d",&s); if(s==-1) { break; } if(s==0) { printf("0\n"); } else { sum=0; for(i=1; i<=s; ++i) { sum+=pow(3,gcd(i,s)); } if(s%2==0) { sum+=pow(3,(s-2)/2+2)*(s/2)+pow(3,(s/2))*(s/2); } else { sum+=pow(3,(s-1)/2+1)*s; } ans=sum/(2*s); printf("%lld\n",ans); } } return 0; }
POJ 2154 Color
这题就不是简单的套用公式就可以过了,由于数据量很大,所以我们就需要使用筛素数结合欧拉函数求解的方式优化复杂度。而且数据范围的原因,很多人为了图省事,确保不会吵范围就用long long定义了事,却发现TLE,所以在写这题是必须还是要使用int定义,而且需要在很多地方取模。注意:快速幂部分取模一定要频繁,每个数在进行运算之前都需要取模。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<set> #include<vector> #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(a,b,i) for(i=a;i<=b;++i) #define For(a,b,i) for(i=a;i<b;++i) using namespace std; inline void RD(int &ret) { char c; do { c=getchar(); } while(c<'0'||c>'9'); ret=c-'0'; while((c=getchar())>='0'&&c<='9') { ret=ret*10+(c-'0'); } } inline void OT(int a) { if(a>=10) { OT(a/10); } putchar(a%10+'0'); } int N; int p(int x,int y)//取模一定要反复,我就是因为这个WA的 { int res=1; while(y>0) { if(y%2==1) { res=(res%N)*(x%N)%N; } x=(x%N)*(x%N)%N; y/=2; } return res%N; } int e(int n) { int ans=1,i; for(i=2; i*i<=n; i++) { if(n%i==0) { ans*=i-1; n/=i; while(n%i==0) { ans*=i; n/=i; } } } if(n>1) { ans*=n-1; } return ans; } int main() { int i,t,s; __int64 sum; RD(t); while(t--) { scanf("%d%d",&s,&N); sum=0; for(i=1; i*i<=s; ++i) { if(s%i==0) { sum=(sum+e(s/i)%N*p(s,i-1))%N;//这是求polya计数的通用优化方式 if(i*i!=s) { sum=(sum+e(i)%N*p(s,s/i-1))%N;//也要注意取模方式 } } } printf("%I64d\n",sum%N); } return 0; }
POJ 2888 Magic Bracelet
超好的组合题,这题是burnside的范围,因为burnside求有限制条件的组合数是很有效果的。这题用到了很多知识burnside+矩阵乘+矩阵快速幂+快速幂取模+欧拉函数+筛素数法+离散数学的知识。运用离散数学的知识将珠子的组合关系建图,转化为矩阵就是1为a和b可联通,0为a和b不可相联。而此矩阵的k次方就代表了经过k条路到达的方案数。
然后再结合欧拉函数优化就可以得到答案了。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<set> #include<vector> #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(a,b,i) for(i=a;i<=b;++i) #define For(a,b,i) for(i=a;i<b;++i) #define N 9973 using namespace std; inline void RD(int &ret) { char c; do { c=getchar(); } while(c<'0'||c>'9'); ret=c-'0'; while((c=getchar())>='0'&&c<='9') { ret=ret*10+(c-'0'); } } inline void OT(int a) { if(a>=10) { OT(a/10); } putchar(a%10+'0'); } int s,m,k,tx[11][11],ty[11][11],tz[11][11]; int p(int x,int y)//快速幂取模 { int res=1; x=x%N; while(y>0) { if(y%2==1) { res=(res*x)%N; } x=(x*x)%N; y/=2; } return res%N; } int e(int n)//欧拉函数优化 { int ans=1,i; for(i=2; i*i<=n; i++) { if(n%i==0) { ans*=i-1; n/=i; while(n%i==0) { ans*=i; n/=i; } } } if(n>1) { ans*=n-1; } return ans%N;//注意取模,不然会超 } void mat(int a[11][11],int b[11][11])//矩阵乘 { int d[11][11],i,j,l; mem(d,0); For(0,m,i) { For(0,m,j) { For(0,m,l) { d[i][j]=(d[i][j]+a[i][l]*b[l][j])%N; } } } For(0,m,i) { For(0,m,j) { a[i][j]=d[i][j]; } } } int g(int x) { mem(ty,0); int i,j,ans; For(0,m,i) { For(0,m,j) { tz[i][j]=tx[i][j]; } } For(0,m,i) { ty[i][i]=1; } while(x>0)//矩阵快速幂 { if(x%2==1) { mat(ty,tz); } mat(tz,tz); x/=2; } ans=0; For(0,m,i) { ans=(ans+ty[i][i])%N; } return ans; } int main() { int i,j,t,a,b; int sum; RD(t); while(t--) { RD(s); RD(m); RD(k); For(0,m,i) { For(0,m,j) { tx[i][j]=1; } } For(0,k,i) { RD(a); RD(b); a--; b--; tx[a][b]=tx[b][a]=0;//建图 } sum=0; for(i=1; i*i<=s; ++i) { if(s%i==0)//其他过程与上题类似 { sum=(sum+(e(s/i)*g(i))%N)%N; if(i*i!=s) { sum=(sum+(e(i)%N*g(s/i))%N)%N; } } } printf("%d\n",(sum*(p(s,N-2)%N))%N); } return 0; }
burnside&&polya还有很多神奇的应用,希望可以与大家多多交流经验~