POJ训练记录1:置换群
2016-05-21 14:00
344 查看
1、POJ 3270 Cow Sorting
传送门题意
给出一个序列,交换两个数的代价是这两个数的和,问将这个序列排成升序序列的代价。
题解
这是一个入门的置换问题。
首先知道第i大的数最终应该换到第i个位置。假设有一些数,这些数通过一次移位可以使所有的数都换到它应该在的位置,那么将这些数的集合称之为一个整数群,群的大小定义为集合内数的个数。整个序列由大小不同的若干群组成。
考虑将一个群排成升序的代价,假设群的大小为k,有两种交换的方法:
1、群里换,拿群里最小的数t与其他每个数交换,共k-1次,花费为:tmp1=sum+(k-2)*t。
2、将这个数列最小的数m,拉入这个群,与该群最小的数t交换,然后用这个最小的数与其他数交换k-1次,然后再将m与t换回来,这样花费为:tmp2=sum+t+(k+1)m。
显然这个群交换的代价为min(tmp1,tmp2)。
我们利用计数排序,得到每一个数应该在的位置,然后找到每一个群,利用上面的方法计算即可。
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int max_n=1e5+5; const int INF=2e9; int n,Max,Min,j,k,t,sum,ans; int a[max_n],cnt[max_n]; bool used[max_n]; int main() { scanf("%d",&n); Min=INF; for (int i=1;i<=n;++i) { scanf("%d",&a[i]); cnt[a[i]]++; if (a[i]>Max) Max=a[i]; if (a[i]<Min) Min=a[i]; } for (int i=1;i<=Max;++i) cnt[i]+=cnt[i-1]; for (int i=1;i<=n;++i) if (!used[i]) { j=i; t=a[i]; k=sum=0; while (!used[j]) { k++; if (t>a[j]) t=a[j]; sum+=a[j]; used[j]=true; j=cnt[a[j]]; } if (1<k) ans+=sum; if (2<k) { int tmp1=(k-2)*t; int tmp2=(k+1)*Min+t; ans+=min(tmp1,tmp2); } } printf("%d\n",ans); }
2、POJ 2369 Permutations
传送门题意
给出一个序列,问最少经过几次置换得到升序。
题解
计算出各个群的大小然后取最小公倍数即可。
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int max_n=1005; int n,j,k,tot; int a[max_n],cnt[max_n],final[max_n]; bool used[max_n]; inline int gcd(int a,int b) { if (!b) return a; else return gcd(b,a%b); } int main() { scanf("%d",&n); for (int i=1;i<=n;++i) { scanf("%d",&a[i]); cnt[a[i]]++; } for (int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for (int i=1;i<=n;++i) if (!used[i]) { j=i; k=0; while (!used[j]) { ++k; used[j]=true; j=cnt[a[j]]; } final[++tot]=k; } for (int i=2;i<=tot;++i) { int t=gcd(final[i-1],final[i]); final[i]=final[i-1]*final[i]/t; } printf("%d\n",final[tot]); }
3、POJ 1026 Cipher
传送门题意
给出置换的规则,ai表示i位置经过一次置换变成ai位置上的字符。给出初始的字符串,问经过s次置换后的字符串是什么。
题解
计算出群的大小,知道置换次数之后判断最终置换到哪个位置即可。
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int max_n=1005; int n,j,k,tot; int a[max_n],cnt[max_n],final[max_n]; bool used[max_n]; inline int gcd(int a,int b) { if (!b) return a; else return gcd(b,a%b); } int main() { scanf("%d",&n); for (int i=1;i<=n;++i) { scanf("%d",&a[i]); cnt[a[i]]++; } for (int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for (int i=1;i<=n;++i) if (!used[i]) { j=i; k=0; while (!used[j]) { ++k; used[j]=true; j=cnt[a[j]]; } final[++tot]=k; } for (int i=2;i<=tot;++i) { int t=gcd(final[i-1],final[i]); final[i]=final[i-1]*final[i]/t; } printf("%d\n",final[tot]); }
4、POJ 1721 CARDS
传送门题意
每次置换的规则是,a[i]变为a[a[i]],给出末状态以及置换的次数,求初状态。
题解
我的做法比较奇怪,是推出了一个由末状态到初状态的每一步的规律,然后做S次就好了。
网上的标解是说经过一定的次数会出现循环的情况,暴力找出循环节然后判断是那种状态就行了。
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int max_n=1005; int n,j,k,t,s; int cnt[max_n],nxt[max_n],ans[max_n]; bool used[max_n]; int main() { scanf("%d%d",&n,&s); for (int i=1;i<=n;++i) scanf("%d",&cnt[i]); t=(n+1)/2; for (int i=1;i<=s;++i) { memset(used,0,sizeof(used)); j=1; k=0; while (!used[j]) { ++k; nxt[k]=j; used[j]=true; j=cnt[j]; } for (int i=1;i<=k;++i) { int pos=nxt[(i+t-1)%k+1]; ans[pos]=cnt[nxt[i]]; } for (int i=1;i<=k;++i) cnt[i]=ans[i]; } for (int i=1;i<=n;++i) printf("%d\n",ans[i]); }
5、POJ 1286 Necklace of Beads
传送门题意
有n个珠子的一个项链,求将珠子染成红色蓝色或绿色的不同的方案数(考虑旋转和翻转)
题解
暴力艹标算。
网上有一种很神的结论,但是刚开始不会,就暴力敲了所有的置换,然后利用Burnside和Polya直接算,时间(2n^2)
答案为1m∑i=1mkc1(ai)(m为置换数,k为颜色数)
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define LL long long const int max_n=30; const int max_m=max_n*2; int n,m,t,k,L,R; int a[max_m][max_n],pre[max_n],nxt[max_n],st[max_n]; bool used[max_n]; LL ans; inline void clear() { memset(a,0,sizeof(a)); m=ans=0; } inline LL fast_pow(LL a,int p) { LL ans=1; for (;p;p>>=1,a*=a) if (p&1) ans*=a; return ans; } inline LL calc(int x) { memset(used,0,sizeof(used)); int cnt=0,p; for (int i=1;i<=n;++i) if (!used[i]) { p=i; cnt++; while (!used[p]) { used[p]=true; p=a[x][p]; } } LL ans=fast_pow(3,cnt); return ans; } int main() { while (~scanf("%d",&n)) { if (n==-1) return 0; if (!n) { printf("0\n"); continue; } clear(); for (int i=2;i<=n;++i) pre[i]=i-1; pre[1]=n; for (int i=1;i<n;++i) nxt[i]=i+1; nxt =1; for (int i=1;i<=n;++i) st[i]=i; for (int i=0;i<n;++i) { ++m; for (int j=1;j<=n;++j) a[m][j]=(j+i-1)%n+1; } if (n%2==0) { for (int i=1;i<=n/2;++i) { ++m; t=n/2-1; for (int j=1;j<=n;++j) a[m][j]=st[j]; L=R=i; for (int j=1;j<=t;++j) { L=pre[L]; R=nxt[R]; swap(a[m][L],a[m][R]); } ++m; t=n/2; for (int j=1;j<=n;++j) a[m][j]=st[j]; L=i+1; R=i; for (int j=1;j<=t;++j) { L=pre[L]; R=nxt[R]; swap(a[m][L],a[m][R]); } } } else { t=n/2; for (int i=1;i<=n;++i) { ++m; for (int j=1;j<=n;++j) a[m][j]=st[j]; L=R=i; for (int j=1;j<=t;++j) { L=pre[L]; R=nxt[R]; swap(a[m][L],a[m][R]); } } } for (int i=1;i<=m;++i) ans+=calc(i); ans/=m; printf("%lld\n",ans); } }
6、POJ 2409 Let it Bead
传送门题意
有n个珠子的一个项链,求将珠子染成至多k种颜色的不同的方案数(考虑旋转和翻转)
题解
题面基本上和上道题一样,学习了新的姿势,也是比较厉害的一个结论,具体见:http://www.cnblogs.com/DrunBee/archive/2012/09/10/2678378.html
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define LL long long LL n,k,ans; inline LL gcd(LL a,LL b) { if (!b) return a; else return gcd(b,a%b); } inline LL fast_pow(LL a,LL p) { LL ans=1; for (;p;p>>=1,a*=a) if (p&1) ans*=a; return ans; } int main() { while (~scanf("%I64d%I64d",&k,&n)) { if (!n&&!k) return 0; ans=0; for (int i=1;i<=n;++i) ans+=fast_pow(k,gcd(i,n)); if (n%2) { ans+=fast_pow(k,n/2+1)*n; } else { ans+=fast_pow(k,n/2)*n/2+fast_pow(k,n/2+1)*n/2; } printf("%I64d\n",ans/(2*n)); } }
7、POJ 2154 Color
传送门题意
有n个珠子的一个项链,求将珠子染成至多n种颜色的不同的方案数(考虑旋转和翻转)
这道题和上道题的不同就在于n的范围为1e9
题解
用到上一题的结论,本题的答案为
L=1n∑0<=k<nngcd(k,n)
=1n∑d|nnd∑0<=k<n[gcd(k,n)=d]
=∑d|nnd−1∑0<=k<n[gcd(kd,nd)=1]
=∑d|nnd−1∑0<=k<nd[gcd(k,nd)=1]
=∑d|nnd−1ϕ(nd)
刚开始狂T不止,大概是LL的原因吧。
学习了黄学长先筛质数然后根n求phi的姿势,挺不错的。
代码
#include<iostream> #include<cstring> #include<cstdio> #include<ctime> #include<cmath> using namespace std; int T,n,Mod,ans; int prime[1000005],p[1000005]; inline void _prime() { for (int i=2;i<=1000000;++i) { if (!p[i]) prime[++prime[0]]=i; for (int j=1;j<=prime[0]&&i*prime[j]<=1000000;++j) { p[i*prime[j]]=1; if (i%prime[j]==0) break; } } } inline int _phi(int x) { int ans=x; for (int i=1;prime[i]<=sqrt(x);++i) if (x%prime[i]==0) { ans=(ans-ans/prime[i]); while (x%prime[i]==0) x/=prime[i]; } if (x!=1) ans=(ans-ans/x); return ans%Mod; } inline int fast_pow(int a,int p) { int ans=1; a%=Mod; for (;p;p>>=1,a=a*a%Mod) if (p&1) ans=ans*a%Mod; return ans; } int main() { _prime(); scanf("%d",&T); while (T--) { scanf("%d%d",&n,&Mod); ans=0; for (int i=1;i*i<=n;++i) if (n%i==0) { ans=(ans+_phi(i)*fast_pow(n,n/i-1)%Mod)%Mod; if (i*i!=n) ans=(ans+_phi(n/i)*fast_pow(n,i-1)%Mod)%Mod; } printf("%d\n",ans); } }
相关文章推荐
- 作业_java基础第十天_集合
- telnet某个ip端口,如果 能telnet通,怎么退出呢
- java-关于文件操作-输出流的使用
- sql入门
- 1007 Problem G
- 动态规划法实现fibonacci数列求解
- 欢迎使用CSDN-markdown编辑器
- 办法总比问题多
- 求最短路径———Dijkstra算法和Floyd算法
- CentOS 6.4 服务器版安装教程(超级详细图解)
- js滚动到底部事件
- XML解析之SAX解析技术案例
- C语言结构体占用空间内存大小解析
- js滚动到底部事件
- fiddler如何抓取APP的流
- adaptive color attributes for tracking翻译
- C#做网站公共方法(20160521)
- 屌丝逆袭成长记:为您讲解泌阳人李帅背后的秘密
- 开发过程中的常用Eclipse插件
- Java EE中Srevlet的使用