UVA10294项链和手镯(等价类计数问题)

题意:
      给你一串珠子(连接成了一个环),共有n个珠子组成,你有t种颜色,现在你来给这个珠子染色,问染成项链有多少种方法?染成手镯有多少种方法?在项链里,经过顺时针旋转后相同的算一个,在手镯里,经过顺时针旋转或者沿着对称轴兑换后一样的算一个。


思路:
      比较典型的等价类计数问题,我们定义两个变量,a是旋转的总个数,b是翻转的总个数,那么根据Burnside和Polya定理,a = C[0] + C[1] + C[2] +..+C[n-1];
C[i]表示的是顺时针移动i个后的种类数,C[i] = t^w,t是颜色种类,w是循环节个数,在这个题目里,旋转是的循环节个数为gcd(i ,n);至于为什么可以自己想,想不通的话可以想想杭电上那个切蛋糕的题目,那么C[i] = t^gcd(i ,n)这样就能求出各个C[i]然后求出a,此时的相连的答案已经出来了,就是a/n,那么b呢?b可以分情况讨论,如果n是奇数那么对角线一共有n条,每次可以分出来(n+1)个循环节,那么b = n * t ^ ((n + 1)/2)如果n是偶数的话有两种情况,不穿过任何点的为 n/2*t(n/2) 穿过两个点的对角线为n/2*(n/2+1)那么此时的b=n/2*(t^(n/2) + t^(n/2+1)),那么手镯的种类为(a+b)/(n*2).


这里在解释下上面的那两个定理,那两个定理是求等价类计数问题的常用定理,大体意思就是说种类数等于所有可能置换方法的方法数的平均数,而每一个置换方法的个数等于颜色个数的循环节次幂,循环节就是置换里面的那个循环节。


#include<stdio.h>


long long gcd(long long a ,long long b)
{
   return a % b == 0 ? b : gcd(b ,a % b);
}




int main ()
{
   long long pow[60];
   long long n ,t ,i;
   long long a ,b;
   while(~scanf("%lld %lld" ,&n ,&t))
   {
      pow[0] = 1;
      for(i = 1 ;i <= n ;i ++)
      pow[i] = pow[i-1] * t;
      a = 0;
      for(i = 0 ;i < n ;i ++)
      a += pow[gcd(i ,n)];
      if(n & 1) b = n * pow[(n+1)/2];
      else b = n / 2 * pow[n/2] + n / 2 * pow[n/2 + 1]; 
      printf("%lld %lld " ,a / n ,(a + b) / n / 2);
   }
   return 0;
}     





原文地址:https://www.cnblogs.com/csnd/p/12062588.html