POJ-1282 庆典的日期 置换群+模线性方程组[好题]

  题目链接:http://poj.org/problem?id=1282

  终于把这道题目给A了。

  先来看看特殊情况:如果p=1的话,那就很容易做了,直接求每个循环节的长度,然后再求最小公倍数就行了。但是p!=1呢?如果依葫芦画瓢,通过n个置换去找循环节,然后再去求最小公倍数,那么难点就在找循环节上,很难处理,因此考虑换一种方法。

  NOI2005论文,潘震皓的<置换群快速幂运算 研究与探讨>上有详细介绍,主要是对置换进行一个变化,然后去枚举答案。

  1 //STATUS:C++_AC_0MS_452KB
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<math.h>
  6 #include<iostream>
  7 #include<string>
  8 #include<algorithm>
  9 #include<vector>
 10 #include<queue>
 11 #include<stack>
 12 #include<map>
 13 using namespace std;
 14 #define LL __int64
 15 #define pii pair<int,int>
 16 #define Max(a,b) ((a)>(b)?(a):(b))
 17 #define Min(a,b) ((a)<(b)?(a):(b))
 18 #define mem(a,b) memset(a,b,sizeof(a))
 19 #define lson l,mid,rt<<1
 20 #define rson mid+1,r,rt<<1|1
 21 const int N=210,INF=0x3f3f3f3f,MOD=10000,STA=8000010;
 22 const LL LNF=0x3f3f3f3f3f3f3f3f;
 23 const double DNF=1e13;
 24 
 25 LL a[N],m[N];
 26 int num[N][N],A[N][N],D[N][N],vis[N],vis2[N];
 27 int n,p;
 28 
 29 void exgcd(LL a,LL b,LL& d,LL& x,LL& y)
 30 {
 31     if(!b){d=a;x=1;y=0;}
 32     else {exgcd(b,a%b,d,y,x);y-=x*(a/b);}
 33 }
 34 
 35 LL Modline(int n)
 36 {
 37     LL d,x,y,A,M,Mod;
 38     A=a[n-1],M=m[n-1];
 39     n--;
 40     // m1*x-m2*y=a2-a1
 41     while(n--){
 42         exgcd(M,m[n],d,x,y);
 43         if((A-a[n])%d!=0){
 44             return -1;
 45         }
 46         Mod=m[n]/d;
 47         x=(x*((a[n]-A)/d)%Mod+Mod)%Mod;
 48         A+=M*x;
 49         M=M/d*m[n];
 50     }
 51     return A;
 52 }
 53 
 54 int find(int T[],int C[])
 55 {
 56     int i,j,u,cnt=0,d,l,t,ok;
 57     mem(vis,0);
 58     for(i=0;i<n;i++){
 59         if(!vis[i]){
 60             l=-1,d=0;u=i;
 61             while(!vis[u]){
 62                 vis[u]=1;
 63                 mem(vis2,0);
 64                 for(t=ok=0,j=u;!vis2[j];t++,j=T[j]){
 65                     vis2[j]=1;
 66                     if(j==C[u]){ok=1;break;}
 67                 }
 68                 if(l==-1)l=t;
 69                 if(!ok || l!=t)return 0;
 70                 u=C[u];
 71                 d++;
 72             }
 73             a[cnt]=l,m[cnt++]=d;
 74         }
 75     }
 76     return cnt;
 77 }
 78 
 79 int main()
 80 {
 81  //   freopen("in.txt","r",stdin);
 82     int i,j;
 83     LL ans,x,t;
 84     while(~scanf("%d%d",&n,&p))
 85     {
 86         for(i=0;i<n;i++){
 87             for(j=0;j<p;j++){
 88                 scanf("%d",&num[j][i]);
 89                 num[j][i]--;
 90             }
 91         }
 92         for(i=0;i<n;i++)A[0][i]=num[0][i];
 93         for(i=1;i<p;i++){
 94             for(j=0;j<n;j++){
 95                 A[i][j]=num[i][A[i-1][j]];
 96             }
 97         }
 98         for(i=0;i<p;i++){
 99             for(j=0;j<n;j++){
100                 D[i][A[i][j]]=j;
101             }
102         }
103 
104         ans=LNF;
105         for(i=0;i<p;i++){
106             if(t=find(A[p-1],D[i])){
107                 if((x=Modline(t))!=-1){
108                     ans=Min(ans,x*p+i+1);
109                     if(ans>=1e9){ans=LNF;break;}
110                 }
111             }
112         }
113 
114         if(ans!=LNF)printf("%I64d\n",ans);
115         else printf("No one knows.\n");
116     }
117     return 0;
118 }

 

转载于:https://www.cnblogs.com/zhsl/archive/2013/04/13/3018380.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值