置换群小结(未完结补充中

1.poj 2369

求最小循环节,先找出所有循环节,最后跑一遍lcm。

int lcm(int x,int y)
{
    return x*y/__gcd(x,y);
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        c[i]=i;
    }
    int ans=1;
    for(int i=1;i<=n;i++)
    {
        int cnt=0,j=i;
        while(!vis[j])
        {
            cnt++;
            vis[j]=1;
            j=c[a[j]];
        }
      //  cout<<cnt<<endl;
        if(cnt)
            ans=lcm(ans,cnt);
    }
    cout<<ans<<endl;
}
2. poj 1026

题意:给你一个长度为n的置换,然后给你m是下面做置换的次数,再给你个字符串,长度为n,空的地方用空格,然后求置换后的字符串

先把每个循环找出来,然后对每个循环,只要做t=m%(循环的阶数)这些就行了,然后就在循环里面找当前这个数后t个是什么,然后代换就行

int a[maxx],c[maxx],vis[maxx],n;
vector<int>Q[maxx];
char s1[maxx],s2[maxx];
int main()
{
   // freopen("in.txt", "r", stdin);
    while(cin>>n&&n)
    {
        int cnt=0;
        me(vis,0);
        for(int i=1;i<=n;i++)
        {
            Q[i].clear();
            cin>>a[i];
        }
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                vis[i]=1;
                cnt++;
                Q[cnt].push_back(i);
                int k=a[i];
                while(i!=k)
                {
                    vis[k]=1;
                    Q[cnt].push_back(k);
                    k=a[k];
                }
            }
        }
        int t;
        while(cin>>t&&t)
        {
            getchar();
            gets(s1+1);
            int len=strlen(s1+1);
            for(int i=len+1;i<=n;i++)
                s1[i]=' ';
            s1[n+1]='\0';
            me(s2,0);
            for(int i=1;i<=cnt;i++)
            {
                int v=Q[i].size();
                for(int j=0;j<v;j++)
                    s2[Q[i][(j+t)%v]]=s1[Q[i][j]];
            }
            s2[n+1]='\0';
            cout<<s2+1<<endl;
        }
        cout<<endl;
    }
}
3. poj 1721

题意:第i个位置的牌是a[i],一次交换后第i个位置的牌变成a[a[i]]。序列所有位置经过一次交换为一次交换,已知交换m次之后的序列,求原先序列

暴力o(n) 先存最初状态 然后再for 直到跟原状态一样找到循环节。则再进行res - s % res 次交换即可得到原序列


void GetNext(int n)  
{  
    for(int i = 1; i <= n; i++)  
        ArrayB[i] = ArrayA[ArrayA[i]];  
    for(int i = 1; i <= n; i++)  
        ArrayA[i] = ArrayB[i];  
}  
  
bool IsSame(int n)  
{  
    for(int i = 1; i <= n; i++)  
        if(ArrayB[i]!=Array[i])  
            return false;  
    return true;  
}  
int main()  
{  
    int n,s;  
    while(~scanf("%d%d",&n,&s))  
    {  
        for(int i = 1; i <= n; i++)  
        {  
            scanf("%d",&Array[i]);  
            ArrayA[i] = Array[i];  
        }  
        int res;  
        for(res = 1;;res++)  
        {  
            GetNext(n);  
            if(IsSame(n))  
                break;  
        }  
        int m = res - s % res;  
        int num = 0;  
        while(num < m)  
        {  
            GetNext(n);  
            num++;  
        }  
        for(int i = 1; i <= n; i++)  
            printf("%d\n",ArrayB[i]);  
    }  
    return 0;  
}  
4. poj 3128
题意:给你一行共 26 个字母,代表一个置换。问:这个置换能否为某个置换平方的结果。

结论一: 一个长度为 l 的循环 T,l 是 k 的倍数,则 T^k 是 k 个循环的乘积,每个
循环分别是循环 T 中下标 i mod k=0,1,2… 的元素按顺序的连接。 
结论二:一个长度为 l 的循环 T,gcd(l,k)=1,则 T^k 是一个循环,与循环 T 不一
定相同。
结论三:一个长度为 l 的循环 T,T^k 是 gcd(l,k)个循环的乘积,每个循环分别是循
环 T 中下标 i mod gcd(l,k)=0,1,2… 的元素的连接。 

这倒题中,应用上边的结论来判断这个置换是否为某个置换平方的结果。
一个置换平方可得到:循环节为奇数的置换的平方仍为奇数项的置换,循环节为偶数
的置换的平方分裂成了两个循环节相同的置换。
那么当前置换是由什么置换而来的:
对于奇数项的置换,有可能是由奇数项的置换平方得到的,也有可能是有偶数项的置
换平方得到的。对于偶数项的置换,它一定是原始置换为偶数项的置换分裂得到的。
而且当前置换中偶数项的置换一定成对出现。
那么问题就变为了:对于偶数项的置换,是否成对出现。
那么,我们现在查找 26 个字母的所有置换,计算出每个置换的循环节长度。统计循
环节长度的个数。遍历查找偶数项的置换数目,如果出现奇数,则输出"No",否则
输出"Yes"。

int main()  
{  
    int T;  
    scanf("%d",&T);  
    while(T--)  
    {  
        memset(s,0,sizeof(s));  
        memset(vis,false,sizeof(vis));  
        memset(Cnt,0,sizeof(Cnt));  
        scanf("%s",s);  
        for(int i = 0; i < 26; ++i)  
            ss[i] = s[i] - 'A';  
        int flag = 1;  
        for(int i = 0; i < 26; ++i)  
        {  
            if(vis[i] == 0)  
            {  
                vis[i] = 1;  
                int temp = ss[i];  
                int num = 1;  
                while(temp != i)  
                {  
                    vis[temp] = 1;  
                    temp = ss[temp];  
                    num++;  
                }  
                Cnt[num]++;  
            }  
        }  
  
        for(int i = 2; i < 26; i+=2)  
            if(Cnt[i]%2)  
            {  
                flag = 0;  
                break;  
            }  
  
        if(flag)  
            printf("Yes\n");  
        else  
            printf("No\n");  
  
    }  
  
    return 0;  
}  
5. poj 2409

题意:给定M种颜色的珠子,每种颜色珠子的个数均不限,将这些珠子做成长度为N的项链。问能做成多少种不重复的项链,最后的结果不会超过int类型数据的表示范围。并且两条项链相同,当且仅当两条项链通过旋转或是翻转后能重合在一起,且对应珠子的颜色相同。

解题思路:
Polya定理的应用。先来看Polya定理。
Polya定理:设 G = {a1,a2,…,ag}是 N 个对象的置换群,用 M 种颜色给这 N 个
对象着色,则不同的着色 方案数为:
                  |G|^(-1) * {M^c(a1) + M^c(a2) + … + M^c(ag)}。
其中 c(ai)为置换 ai 的循环节数,( i = 1,2,…,g )。
对于这道题,直接用Polya定理求解,找出所有的置换,并求出置换的循环节数。然后
根据上边公式求出 M^c(ai) 的总和,再除以置换群个数。

题中有两种置换方式:
1.旋转置换。分别顺时针旋转 i 个珠子,其循环节长度为 LCM(N,i) / i,循环节数为
N / (LCM(N,i) / i),即 GCD(N,i)。
2.翻转置换。根据 N 的奇偶性分情况讨论。
N为奇数时:
       以第 i 个珠子为顶点和中心翻转,翻转后,第 i 个珠子保持不变,其余珠子两两相
互对换,因为有 N 个珠子,所以有 N 种翻转置换,每种翻转循环节数为 (N+1) / 2。
N为偶数时,有两种翻转方式:
      以两边相对的两个珠子为轴和中心翻转,翻转后,这两个珠子保持不变,其余珠子
两两相互对换,共有 N/2 种翻转置换,每种翻转循环节数为 (N+2) / 2。
      以相邻的珠子中间连线为轴和中心翻转,翻转后,所有珠子两两相互对换,共有 N/2
种翻转置换,每种翻转循环节数为 N/2。
然后根据Polya定理的公式和上述所求求出结果。

int GCD(int a,int b)  
{  
    if(b == 0)  
        return a;  
    return GCD(b,a%b);  
}  
  
int main()  
{  
    while(~scanf("%d%d",&M,&N) && (M||N))  
    {  
        int sum = 0;  
        for(int i = 1; i <= N; ++i)  
        {  
            int tmp = GCD(N,i);  
            sum += (int)(pow(M*1.0,tmp*1.0));  
        }  
        if(N & 1)  
            sum += (int)(N * pow(M*1.0, (N+1)/2.0));  
        else  
        {  
            sum += (int)((N/2) * pow(M*1.0, (N+2)/2.0));  
            sum += (int)((N/2) * pow(M*1.0, N/2.0));  
        }  
        sum = sum/(2*N);  
        printf("%d\n",sum);  
    }  
  
    return 0;  
}  


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值