http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=35397
点击打开链接
解析见 算法入门经典训练指南:
Burnside定理:对于一个置换f,若一个方案s经过置换后不变,则s为一个不动点。一个置换f的不动点数目为C(f),则等价类数目为C(f)的平均值。
Polya定理:置换f可以分解成m(f)个循环的乘积,有k种涂色方案,等价类的个数等于所有置换f的k^m(f)的平均数
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long using namespace std; int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int main() { int n,t; while(cin>>n>>t) { LL a=0; for(int i=0;i<=n-1;i++) a+=pow(t,gcd(i,n)); LL b=0; if(n%2) b=n*pow(t,(n+1)/2); else b=n/2*(pow(t,n/2+1)+pow(t,n/2)); cout<<a/n<<" "<<(a+b)/(2*n)<<endl; } return 0; }
题目:有一串数字,要将它排列成升序,每次可以交换两个数,交换一次的代价为两数之和。要求代价最小。
点击打开链接
例如初始状态为1 8 9 7 6 目标状态为 1 6 7 8 9,可以分解为 (1)(8 6 9 7)第一个循环只有一个元素,证明它在正确的位置,不参与计算,
第二个循环如果按第一种方法来的话,花费为 6+7+8+9+(4-2)*6=42 而第二种花费为 6+7+8+9+6+(4+1)*1=41
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define inf 99999999 using namespace std; struct node { int id; int num; }p[10010]; bool visited[10010]; bool cmp(struct node a,struct node b) { return a.num<b.num; } int main() { int n; cin>>n; int minx=inf; for(int i=0;i<=n-1;i++) { cin>>p[i].num; p[i].id=i; if(p[i].num<minx) minx=p[i].num; } sort(p,p+n,cmp); memset(visited,0,sizeof(visited)); int ans=0; for(int i=0;i<=n-1;i++)//访问循环模板 { if(!visited[i]) { int cnt=1; visited[i]=1; int sum=p[i].num; int l=p[i].id; int minxx=p[i].num; while(l!=i) { cnt++; visited[l]=1; sum+=p[l].num; l=p[l].id; if(minxx>p[l].num) minxx=p[l].num; } ans+=min(sum-minxx+(cnt-1)*minxx,sum+(cnt+1)*minx+minxx); } } cout<<ans<<endl; return 0; }
统计各个循环中的数据模板 struct node { int id;//位置 int num;//数 }p[10010]; sort后的序列为新序列 memset(visited,0,sizeof(visited)); for(int i=0;i<=n-1;i++)//访问循环模板 { if(!visited[i]) { int cnt=1;//统计循环的点个数 visited[i]=1; int sum=p[i].num;//统计循环的数之和 int l=p[i].id; int minxx=p[i].num;//寻找循环中的最小值 while(l!=i) { cnt++; visited[l]=1; sum+=p[l].num; l=p[l].id; if(minxx>p[l].num) minxx=p[l].num; } } }