polya定理应用

来源:互联网 发布:新手淘宝开店教程 编辑:程序博客网 时间:2024/05/23 12:20
 

TT_last 原创,转载请注明。

polya定理不知道可先看潘震皓的 《置换群快速幂运算研究与探讨》,下面是根据论文所做的一些题目。

poj 2409

 很裸的一道polya定理应用题目,

处理的状态有:一个点顺时针旋转 0 ~ n-1 个点

还有就是对称旋转的情况:

当n为偶数时:有两个对应的点相连线 和两条对应线段中点连线 分别形成的对称轴

当n为奇数时:仅有一个点与对应直线形成对称轴。

 

 #include <iostream>#include <cmath>using namespace std;int c,s;int gcd(int a,int b){    if(b == 0) return a;    else  return gcd(b,a%b);}int polya(int n,int m){    int ans,i;    __int64 tmp = 0;    for(i = 0;i < n;i ++)    {       tmp += pow(m*1.0,1.0*gcd(n,i)); //n个循环  移动多少位。     }    if(n&1)    {      tmp += n*pow(m*1.0,(n+1)/2.0);  //n个循环  一个点与对应线段中点连线     }else    {      tmp += n/2*pow(m*1.0,n/2+1)+n/2*pow(m*1.0,n/2);  //n个循环  分别为两个点连线 两条相对线段中点连线。    }    ans = tmp/(2*n);    return ans;}int main(){    while(scanf("%d%d",&c,&s),c|s)    {       printf("%d\n",polya(s,c));    }    return 0;}


 

poj 2154

 /*
 欧拉函数 + 快速幂 + 筛选质因数
*/

原本n很大,如果按照前面的做法去做 从1 ~ n,不断枚举,那肯定要tle,这里需要用欧拉函数优化一下。

根据polya定理:方案数(先不考虑取余):

for(i = 0;i < n;i ++)

   ans += pow(n,gcd(n,i));

从中,如果我们提取相同的gcd(n,i) 和取得它们的个数,那么时间复杂度将会减少很多。

设 m = gcd(n,i);m可以肯定是n的因子,而m = gcd(n,i),的个数就是 1 = gcd(n/m,i/m)的i/m个数,

这个可以用欧拉函数来求 euler(n/i);然后再根据polya定理做一下也就可以了。

 

#include <iostream>using namespace std;const int maxn = 32000;int n,m,tmp;int p[maxn],num,a[400],b[400],ans;bool sign[maxn];void init(){ int i,j; num = 0; p[num++] = 2; for(i = 4;i < maxn;i += 2) sign[i] = true; for(i = 3;i < maxn;i ++) {  if(!sign[i])  {   p[num++] = i;   for(j = i*i;j < maxn;j += i)    sign[j] = true;  } }}void factor(int x){ int i; tmp = 0; for(i = 0;i < num;i ++) {  if(x < p[i]) break;  if(x%p[i] == 0)  {   b[tmp] = 0;   while(x%p[i] == 0)   {    x /= p[i];    b[tmp]++;   }   a[tmp++] = p[i];  } } if(x > 1) b[tmp] = 1,a[tmp++] = x;  }int euler(int x){ int ret = x,i; for(i = 0;i < num;i ++) {  if(x < p[i]) break;  if(x%p[i] == 0)  {   while(x%p[i] == 0) x /= p[i];   ret -= ret/p[i];  } } if(x > 1) ret -= ret/x; return ret;}int pow_m(int a,int b){ int ret = 1,t = a%m; while(b) {  if(b&1) ret = (ret*t)%m;  t = (t*t)%m;  b >>= 1; } return ret;}void dfs(int dep,int sum){ int i,temp; if(dep == tmp) {  ans = (ans + (euler(n/sum)%m)*pow_m(n,sum-1))%m; }else {  for(i = 0,temp = 1;i <= b[dep];temp*=a[dep],i ++)   dfs(dep+1,sum*temp);  }}int main(){ int x; init(); scanf("%d",&x); while(x--) {  scanf("%d%d",&n,&m);  factor(n);  ans = 0;  dfs(0,1);  printf("%d\n",ans); } return 0;}


 poj 3270

/*置换群 最小交换权
第一种状态:polya循环中最小的那个数 去与其它数据交换。
费用: sum+(len-2)*min2;
第二种状态:从外面拿最小的数进来参与交换,最后换回去:
费用: sum+min2+(len+1)*min;
换进来 1 + 循环 len -1 + 出去 1 = len+1;
min2 出去1 + 进来 1 = 2;
*/

#include <iostream>#include <algorithm>using namespace std;const int inf = 1000000000;const int maxn = 10005;int v[maxn],b[maxn],n;int hash[maxn*10];bool sign[maxn];int main(){    int ans,min,min2,sum;    int i,j,t,len;    while(scanf("%d",&n)!=EOF)    {       min = inf;       for(i = 0;i < n;i ++)        {             scanf("%d",&v[i]);             b[i] = v[i];             sign[i] = false;             if(min > v[i]) min = v[i];       }       sort(b,b+n);       for(i = 0;i < n;i ++) hash[b[i]] = i;       ans = 0;       for(i = 0;i < n;i ++)       {          if(!sign[i])          {             t = i;    min2 = v[i];             len = 0;sum = 0;             sign[i] = true;             do{                len ++;                sum += v[t];                sign[t] = true;                if(min2 > v[t]) min2 = v[t];                t = hash[v[t]];             }while(t != i);             int ans1,ans2;             ans1 = sum+(len-2)*min2;             ans2 = sum+min2+(len+1)*min;             ans += ans1 > ans2 ? ans2:ans1;          }       }       printf("%d\n",ans);    }    return 0;}


 

poj 1026

题目是给出一个密钥,然后给出k和一段字符串,然后根据上面的密钥不处理字符串k次。处理的方法是:Character in the message at the position i is written in the encoded message at the position ai, where ai is the corresponding number in the key。

如果直接模拟肯定是tle的,我的做法是,先求出每一个循环,可以肯定每一个循环的周期就是那个循环的长度。

然后k%len[i] 得出每一个循环要做的处理次数,然后对于每一个循环都处理一下,最后输出结果就行了。

 

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn = 205;int v[maxn],n,k,a[maxn],len[maxn],num;bool sign[maxn];char c[maxn];int main(){    int i,j,l,t; char tmp; //freopen("1.txt","w",stdout);    while(scanf("%d",&n),n)    {    num = 0;    len[0] = 0;       for(i = 1;i <= n;i ++)     {    scanf("%d",&v[i]);    sign[i] = false;    len[i] = 0;    }    for(i = 1;i <= n;i ++)   //求出每一个循环 的长度 和出发点。    {     if(!sign[i])     {      t = i;      do      {       sign[t] = true;       len[num] ++;    t = v[t];      }while(t != i);      a[num++] = i;     }    }       while(scanf("%d",&k),k)       {           getchar();           gets(c+1);           l = strlen(c+1);     for(i = l+1;i <= n;i ++) c[i] = ' ';     c[i] = '\0';     for(i = 0;i < num;i ++) //对每一个循环都进行k次变换,当然根据周期为len[num]取余     {        l = k % len[i]; //进行l次循环。 复杂度最多 O(n^2) = 40000,可以接受。      for(j = 0;j < l;j ++)     {      t = a[i];      tmp = c[t];      do      {       swap(tmp,c[v[t]]);       t = v[t];      }while(t != a[i]);     }     }     puts(c+1);       }       puts("");    }    return 0;}


 

poj 1721

论文后面的原题来的。将置换群用Jordan置换表示。

 #include <iostream>using namespace std;const int maxn = 1005;int v[maxn],v2[maxn],n,s;int main(){    int i,j,t;    while(scanf("%d%d",&n,&s)!=EOF)    {       for(i = 1;i <= n;i ++) scanf("%d",&v[i]);       j = i = 1;       v2[j] = 1;       while(v[i] != 1)       {           v2[++j] = v[i];           i = v[i];       }       t = 1;       while(s > 0)       {          t = (t<<1)%n;          s --;       }       v[1] = j = v2[1];       for(i = 2;i <= n;i ++)       {          j += t;          if(j > n) j -= n;          v[j] = v2[i];       }       for(i = 1;i < n;i ++) v2[v[i]] = v[i+1];       v2[v[n]] = v[1];       for(i = 1;i <= n;i ++) printf("%d\n",v2[i]);    }    return 0;}


 

poj 3128

重要的句子:Write a program that takes a permutation of the English alphabet as input and decides if it may be the result of performing some permutation twice.
判断输入的句子能不能是某一个字串的平方。分两种情况:

当循环中有偶数个数的时候,平方后变成两个相等的循环。当循环奇数时,循环没有裂开。论文中gcd(i,n)可得。所以判断循环中相同数目的偶数循环是不是偶数,是的话就可以变成某个串的平方了。

#include <iostream>using namespace std;const int maxn = 30;bool sign[maxn];int n,a[maxn],num[maxn];char c[maxn];int main(){    int i,j,b;    scanf("%d",&n);    while(n--)    {        scanf("%s",c);        for(i = 0;i < 26;i ++)           a[i] = c[i] - 'A',sign[i] = false,num[i] = 0;        num[26] = 0;        for(i = 0;i < 26;i ++)        {           if(!sign[i])           {               j = i;               b  = 0;               do{                  sign[j] = true;                  b ++;                  j = a[j];               }while(j != i);               num[b] ++;           }        }        for(i = 2;i <= 26;i +=2)              if(num[i]%2) break;        if(i <= 26) puts("No");        else puts("Yes");    }    return 0;}


 

poj 3590

解题报告在:http://hi.baidu.com/zzningxp/blog/item/240fbc8ba9ee167a9e2fb428.html,参照着写就可以了。

#include <iostream>#include <algorithm>using namespace std;const int maxn = 200;int n;bool sign[maxn];int a[maxn],al,b[maxn],num,best[maxn],bl,ans,mul;void init(){     int i,j;     b[num++] = 2;     for(i = 2;i < maxn;i += 2) sign[i] = true;     for(i = 3;i < maxn;i ++)     {        if(!sign[i])        {            b[num++] = i;            for(j = i*i;j < maxn;j += i)               sign[j] = true;        }     }}void dfs(int sum,int i){     int j;  if(sum < b[i])  {   if(mul > ans)   {    ans = mul;    for(j = 0;j < al;j ++) best[j] = a[j];    bl = al;    while(sum > 0)    {     best[bl++] = 1;     sum --;    }   }  }else  {   dfs(sum,i+1);   for(j = b[i];j <= sum;j *= b[i])   {    a[al++] = j;       mul *= j;    dfs(sum - j,i+1);    al --;       mul /= j;   }  }}int main(){    int t,i,j,l;    init();    scanf("%d",&t);    while(t--)    {        al = bl = 0;        ans = 0;mul = 1;        scanf("%d",&n);        if(n == 1) puts("1 1");        else        {        dfs(n,0);        printf("%d",ans);        sort(best,best+bl);        for(i = 0,j = 1;i < bl;i ++)        {           for(l = j+1;l < j+best[i];l ++) printf(" %d",l);           printf(" %d",j);           j += best[i];        }        printf("\n");        }    }}