TT_last 原创,转载请注明。
polya定理不知道可先看潘震皓的 《置换群快速幂运算研究与探讨》,下面是根据论文所做的一些题目。
很裸的一道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;
}
/*
欧拉函数 + 快速幂 + 筛选质因数
*/
原本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;
}
/*置换群 最小交换权
第一种状态: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;
}
题目是给出一个密钥,然后给出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;
}
论文后面的原题来的。将置换群用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;
}
重要的句子: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;
}
解题报告在: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");
}
}
}