poj 1282 庆典的日期 置换群

  根据题意,N个编号祭祀,N个房间,每个转盘上有P个数字,且每年保证每个房间有且仅有一个祭祀。

  可以转换成 P 个不同的 N元组置换。

  其中   为第i天祭祀位置, 为第 i 天转盘置换

  则 第i天 祭祀所在的房间为      (为什么是先D后T,因为置换的乘法是连接,先 )

  

  题目要求 经过 Y天,得到单位置换, 且总共P个不同的置换所以我们可以得到如下形式:

    (其中 e 为单位置换)

  假设 Y mod p  = k , 则可表示成如下形式:

  

  再转换一下:

   

  令 x = (y - k )/ p ,  , 得到

  上式转换成:

    

   对于  ,   我们通过预处理得出,然后枚举 K

   的 X 次幂,转换成  , N个元组相对应,我们可用通过置换模拟,求出每一个元组的 ( Ri, Ai ) 从而得到一个模同余方程组。

  求解方程组,得到X,然后得出Y。

解题代码:

View Code
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
using namespace std;

typedef long long LL;
const int N = 210;
const LL inf = 1e9;
int A[N][N], T[N][N], D[N][N], p, n;
int r[N], a[N];// X = r[i] ( mod a[i] )

bool find( int Q[], int key, int k, int &rr, int &aa )
{
    int s = Q[k]; 
    rr = -1; aa = 1;
    if( k == key ) rr = 0;
    while( s != k )
    {    
        if( s == key ) rr = aa;
        aa++;
        s = Q[s];
    }
    return (rr == -1) ? false : true;
}
LL ExGcd( LL a, LL b, LL &x, LL &y )
{
    if( b == 0 ) { x=1;y=0; return a;}
    LL r = ExGcd( b, a%b, x, y );
    LL t = x; x = y; y = t-a/b*y;
    return r;
}
LL ModLine( )
{
    LL rr = r[0], aa = a[0];
    for(int i = 1; i < n; i++)
    {
        // r[i] - rr = aa*x + a[i]*y    
        LL x, y, C = r[i]-rr;
        LL d = ExGcd( aa, a[i], x, y );
        if( C%d != 0 ) return -1;
        LL Mod = a[i]/d;
        x = ((x*(C/d)%Mod)+Mod)%Mod;
        rr = rr + aa*x;
        aa = aa*(a[i]/d);
    }
    return rr;
}
int main()
{
    while( scanf("%d%d", &n, &p) != EOF)
    {
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < p; j++)
            {
                scanf("%d", &A[j][i] );
                A[j][i]--; // (0,1,2,...,n-1)
            }
        }
        for(int i = 0; i < n; i++)
            T[0][i] = A[0][i];
        for(int i = 1; i < p; i++)
        { // T[p-1]*T[p-2]*...*T[0]  置换乘法为连接,且左后右先
            for(int j = 0; j < n; j++)
                T[i][j] = A[i][ T[i-1][j] ];
        }
        for(int i = 0; i < p; i++ )
        { // 求逆
            for(int j = 0; j < n; j++)
                D[i][ T[i][j] ] = j;
        }
        LL res = inf;    
        // 枚举 Y % p = k, x = (Y-k)/p 
        for(int k = 0; k < p; k++)
        {// T[p-1]^x = D[k]
            bool flag = true;
            for(int i = 0; (i < n) && flag; i++)
                if( !find( T[p-1], D[k][i], i, r[i], a[i] ) ) flag = false;
            if( !flag ) continue;    
            LL x = ModLine();
            if( ( x != -1 ) && ( x*p+k+1 < res ) )
                res = x*p+k+1;    
        }
        if( res == inf ) puts("No one knows.");
        else    printf("%lld\n", res );
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yefeng1627/p/2844157.html