BestCoder Round #80 1004 hdu 5668 中国剩余定理(m不互质版)



链接:戳这里


Circle 
 Time Limit: 2000/1000 MS (Java/Others)   Memory Limit: 65536/65536 K (Java/Others)
问题描述
\ \ \ \     Fye对约瑟夫游戏十分着迷.

\ \ \ \     她找到了n个同学,把他们围成一个圈,让他们做约瑟夫游戏,然后她得到了一个同学们出圈的编号序列.游戏是这样进行的:以同学1为起点,开始计数,计数到第k个同学,该同学出圈.出圈的同学将不参与之后的计数.

\ \ \ \     如今Fye找到了你,她想让你告诉他满足已知出圈序列的最小的k,如果你回答不上来,她就会很生气然后把你吊打一顿.
输入描述
\ \ \ \     第一行一个数T,为测试数据组数.

\ \ \ \     对每组测试数据,第一行一个数n.

\ \ \ \     第二行nn个数,为同学的出圈序列(第ai个出圈的人,编号为i).

\ \ \ \     输入数据必须是一个11到nn的合法排列.

\ \ \ \     1≤T≤10,2≤N≤20.
输出描述
\ \ \ \     对于每组测试数据,若存在合法的k,输出一个正整数,为合法的最小k,否则输出”Creation August is a SB!“.
输入样例
1
7
7 6 5 4 3 2 1
输出样例
420


思路:

约瑟夫环问题的一般步骤是数k步 然后当前的i出列

已知每一次出环的位置,那么模拟这个过程

  (a[i-1]+k) %mi = ai % mi  其中ai为->在环内数ai还在环内的人  比如当前是123 上 2这个位置出环,那么我只需要2个没有标记的人,数完之后所在的位置  mi为环内还存活的人数

1:(0+k)%n=a1%n

2:(a1+k)%(n-1)=a2%(n-1)

....

3:(a[i-1]+k)%1=an%1

ai的话需要模拟求得,其实也就是数出cnt个人之后第bi个人出环  (出环的顺序已知

显然是中国剩余定理  前提是mi不互质

具体的中国剩余定理解析戳下面

http://yzmduncan.iteye.com/blog/1323599


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef unsigned long long ull;
#define INF (1ll<<60)-1
using namespace std;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
    if(!b){
        d=a;
        x=1;
        y=0;
    } else {
        exgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}
ll gcd(ll a,ll b){
    return b==0 ? a : gcd(b,a%b);
}
/* a*x = 1%m 逆元 */
ll inv(ll a,ll m){
    ll d,x,y;
    exgcd(a,m,d,x,y);
    if(d!=1) return -1;
    return (x%m+m)%m;
}
bool Merge(ll a1,ll m1,ll a2,ll m2,ll &a3,ll &m3){
    ll d=gcd(m1,m2);
    ll c=a2-a1;
    if(c%d!=0) return false;
    c=(c%m2+m2)%m2;
    c/=d;
    m1/=d;
    m2/=d;
    c*=inv(m1,m2);
    c%=m2;
    c*=m1*d;
    c+=a1;
    m3=m1*m2*d;
    a3=(c%m3+m3)%m3;
    return true;
}
ll china_Reminder(int n,ll *a,ll *m){
    ll a1=a[1],m1=m[1];
    ll a2,m2;
    for(int i=2;i<=n;i++){
        ll aa,mm;
        a2=a[i];
        m2=m[i];
        if(!Merge(a1,m1,a2,m2,aa,mm)) return -1;
        a1=aa;
        m1=mm;
    }
    ll t=(a1%m1+m1)%m1;
    if(t==0) return m1;
    return t;
}
ll a[22],b[22];
ll m[22];
int n;
int vis[22];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) m[i]=(ll)n-i+1LL;
        for(int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            b[x]=(ll)i;
        }
        int w=0,cnt=0;
        mst(vis,0);
        for(int i=1;i<=n;i++){
            while(1){
                w=(w%n+n)%n+1;
                if(!vis[w]) cnt++;
                if(w==b[i]) break;
            }
            a[i]=(ll)cnt%m[i];
            vis[w]=1;
            cnt=0;
        }
        ll ans=china_Reminder(n,a,m);
        if(ans==-1) printf("Creation August is a SB!\n");
        else printf("%I64d\n",ans);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值