置换与Polya 计数原理-应用部分

下面的代码只是先写出来,还没有用过。
// polya定理 求解循环节数
const int N=1e3+10;
int per[N];
bool vis[N];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int polya(int n){
    int pos,sum=0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++){
        if(vis[i]==0){
            sum++;
            pos=i;
            while(vis[per[pos]]==0){
                vis[pos]=1;
                pos=per[pos];
            }
        }
    }
    return sum;
}


有一个长度是n的环,
对于旋转,顺时针k或逆时针旋转n-k个单位,那么它对应的循环节的个数是gcd(k, n) (不断相减), 长度是 n/gcd(k,n)
对于翻转,如果n是奇数,那么循环节数:

即(n+1)/2 个
如果是偶数,那么:

即 (n+2)/2  
或者:

即 n/2
置换群的个数:2*n    (顺时针和逆时针)
给定m种颜色的柱子,每一种颜色的珠子的个数是无限的,将这些珠子组成长度为n的项链,问能做成多少种不同的项链?(经过旋转或翻转后得到的两条项链重合在一起对应的柱子的颜色相同算同一种项链)
input: m n
output: answer

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int m,n;
    while(cin>>m>>n){
        int sum=0;
        for(int i=1;i<=n;i++){
            sum+=(int)pow(m*1.0,gcd(i,n)*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.0));
            sum+=(int)(n/2*pow(m*1.0,(n+2)/2.0));
        }
        printf("%d\n",sum/2/n);
    }
    return 0;
}

nyist   280   LK的项链
LK的男朋友送给LK一盒有红、蓝、绿三种颜色的珠子,每种颜色珠子的个数都大于24,现在LK想用这一盒珠子穿出一条项链,项链上的珠子个数为 n(0<=n<=24),请你帮她计算一下一共可以用这一盒珠子可以穿出多少条不同的项链。通过旋转、翻转达到同一种状态的被认为是相同的项 链。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int n;
    while(cin>>n){
        if(n==-1) break;
        if(n==0) {
            puts("0");
            continue;
        }
        LL sum=0;
        for(LL i=1;i<=n;i++){
            sum+=(LL)pow(3.0,gcd(i,n)*1.0);
        }
        if(n&1) sum+=(LL)(n*pow(3.0,(n+1)/2.0));
        else {
            sum+=(LL)(n/2*pow(3.0,n/2.0));
            sum+=(LL)(n/2*pow(3.0,(n+2)/2.0));
        }
        printf("%lld\n",sum/2/n);
    }
    return 0;
}

POJ 3270 Cow Sorting
大意:对一串数字排序,交换两个数字的代价是数字之和,问要完成升序排列需要多大的代价?
分析:原串和交换后的串就是置换的关系。想要置换的代价最小,那么要么是用循环里的最小值作为交换中间值,要么使用一个循环外的最小值进行交换。
设sum是循环内的元素的和,cnt是循环中的元素个数,min1是循环内的最小值,Min是循环外的最小值
例如:
4 1 2 5 3
--3-->
4 2 1 5 3
--4-->
4 2 3 5 1
--6-->
4 2 3 1 5
--5-->
1 2 3 4 5
result=sum+(cnt-2)*min1
又如:
外面最小值:1
1  100 101 99
--100-->
99 100 101 1
--102-->
99 100   1   101
--101-->
99   1    100 101
--100-->
  1  99   100 101
result=sum+(cnt+1) Min+min1.
啊,举例不当,上面的结果不如循环内直接交换,但是有这样的情况:
sum+(cnt+1) Min+min1 < sum+(cnt-2)*min1
(cnt+1)Min<(cnt-3)min1

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N=1e4+5,M=1e5+10;
struct node{
    LL cnt;  //循环节长度
    LL min1;  //循环内的最小值
    LL all;  //循环内元素的和
}s[N];
LL cow[N],ls[N];
bool vis[M];
void dfs(LL start,LL tot,LL n){  //映射关系
    for(int i=0;i<n;i++){
        if(start==cow[i]&&vis[cow[i]]==0){  //vis[cow[i]]==0
            vis[cow[i]]=1;
            s[tot].cnt++;
            s[tot].min1=min(s[tot].min1,cow[i]);
            s[tot].all+=cow[i];
            dfs(ls[i],tot,n);
        }
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int n;
    while(cin>>n){
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++){
            s[i].cnt=0;
            s[i].min1=1<<29;
            s[i].all=0;
        }
        LL tot=0,Min=1<<29;
        for(int i=0;i<n;i++){
            scanf("%lld",&cow[i]);
            ls[i]=cow[i];
            Min=Min<ls[i]?Min:ls[i];
        }
        sort(ls,ls+n);
        for(int i=0;i<n;i++){
            if(!vis[cow[i]])  {
                vis[cow[i]]=1;
                s[tot].all+=cow[i];
                s[tot].cnt++;
                s[tot].min1=min(s[tot].min1,cow[i]);
                dfs(ls[i],tot,n);
                tot++;
            }
        }
        LL ans=0;
        for(int i=0;i<tot;i++){
            ans=ans+s[i].all+min((s[i].cnt-2)*s[i].min1,(s[i].cnt+1)*Min+s[i].min1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

POJ 1286 Necklace of Beads
http://poj.org/problem?id=1286
这题和nyist   280  LK的项链 几乎一样。

POJ 1721 CARDS
http://poj.org/problem?id=1721
大意: 给出洗完牌后的顺序,求出原来的顺序
洗牌规则,例如:
3 2 4 5 1
---------
1 2 3 4 5
--变化后-->:
4 2 5 1 3

问题有点像同余里的逆元
举个例子,1->2->3->1   循环节长度是3,原数是1,那么经过8次变化后,答案是8%3后的变化结果,那么再经过3-2次变化就是原来的数字,所以求解原数就是新数变化 3-8%3次。即cir-s%cir

#include <iostream>
#include <cstdio>
using namespace std;
int c[1005][1005];
int find(int dex,int n){
    int *pr=c[dex-1],*now=c[dex];
    bool ok=1;
    for(int i=1;i<=n;i++){
        now[i]=pr[pr[i]];
        if(now[i]!=c[0][i]) ok=0;
    }
    if(ok) return dex;
    else return find(dex+1,n);
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int n,s;
    while(cin>>n>>s){
        for(int i=1;i<=n;i++){
            scanf("%d",&c[0][i]);
        }
        int cir=find(1,n);
        int dex=cir-s%cir;
        for(int i=1;i<=n;i++){
            printf("%d\n",c[dex][i]);
        }
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值