POJ 3708 高精度 + 置换 + 中国剩余定理

传送门:POJ 3708

题解

(1)设m, k的d进制表示分别为p1p2···pn1,
k1k2···kn2
其中n1, n2是位数

(2)由 f(m) 定义得:

f(m)=ap1dn1+i=2n(bpidni),ap1ϵ[1d,(bpiϵ[0,d),iϵ[2,n)]

(3) 所以 fm 的d进制形式为a p1b p2···b pn1, 由此可知m和 f(m) 的在d进制下的表示位数是相同的, 所以若
fx(m)=k 存在解, 必须满足 n1 = n2

(4)此外, 根据 (3) 还能得到 : fx(m) 的d进制是m的d进制各位上x次置换,首位是a上的置换,其余如果存在是在b上的置换

(5) 现考虑pi->ki,若果pi和ki在i所在置换上在一个环内, 则存在一个最小的置换次数记lefti, 满足pi经过lefti次置换后变成ki, 且之后每经过该环大小(记为modi)次置换,都能变为ki, 所以若存在 fx(m)=k , n1 = n2且 pi和ki iϵ[1,n] )其所在置换内都在一个环中(抽象代数置换知识), 且 x满足:

xlefti(modmodi)iϵ[1,n]

(6) 中国剩余定理两两合并可解

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 105;

int d;
int a[N], b[N];
int aa[410], bb[410], mod[410], left[410], in1[110], in2[110];
char s1[110], s2[110];
/**高精度转d进制*/
void tran(int ten[], int len, int &cnt, int tmp[]){
    cnt = 0;
    int tcnt = 0;
    while(tcnt < len){

        for(int i = tcnt; i < len; ++i){
            ten[i + 1] += (ten[i] % d) * 10;
            ten[i] /= d;
        }/**模拟除法,余数 * 10存在ten[len]中*/

        tmp[cnt++] = ten[len] / 10;
        ten[len] = 0;
        while(ten[tcnt] == 0 && tcnt < len) ++tcnt;
    }
}

void getModAndLeft(int len){/**获取模数和余数*/
    int id = len - 1;
    mod[id] = 1;
    int cnt = 0;
    if(aa[id] == bb[id]) left[id] = 0;
    for(int i = a[aa[id]]; i != aa[id]; i = a[i]){
        ++mod[id];
        ++cnt;
        if(i == bb[id]) left[id] = cnt;
    }

    for(int i = 0; i < id; ++i){
        if(aa[i] == bb[i]) left[i] = 0;
            mod[i] = 1;
            cnt = 0;
        for(int j = b[aa[i]]; j != aa[i]; j = b[j]){
            ++mod[i];
            ++cnt;
            if(j == bb[i]) left[i] = cnt;
        }
    }
    /**printf("test\n");*/
}

ll extend_gcd(ll a, ll b, ll &x, ll &y){
    if(b == 0){
        x = 1;
        y = 0;
        return a;
    }

    ll d = extend_gcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main(){

    while(~scanf("%d", &d), ~d){
        memset(left, -1, sizeof left);
        for(int i = 1; i < d; ++i){
            scanf("%d", a + i);
        }
        for(int i = 0; i < d; ++i){
            scanf("%d", b + i);
        }
        scanf("%s%s", s1, s2);
        int len1 = strlen(s1), cnt1;
        for(int i = len1 - 1; i >= 0; --i) in1[i] = s1[i] - '0';
        int len2 = strlen(s2), cnt2;
        for(int i = len2 - 1; i >= 0; --i) in2[i] = s2[i] - '0';
        tran(in1, len1, cnt1, aa);
        tran(in2, len2, cnt2, bb);
        if(cnt1 == cnt2){/**判断位数*/

            getModAndLeft(cnt1);
            bool f = 0;
            for(int i = 0; i < cnt1; ++i) if(left[i] == -1) f = 1;/**判断是否不在一个环*/
            if(f) puts("NO");
            else{
                ll modNum = mod[0] * 1ll, leftNum = left[0] * 1ll, x, y;
                for(int i = 1; i < cnt1; ++i){/**两两合并*/
                    ll c = left[i] * 1ll - leftNum;
                    ll m1 = modNum, m2 = mod[i] * 1ll;
                    ll d = extend_gcd(modNum, m2, x, y);
                    if(c % d){
                        f = 1;
                        break;
                    }
                    x *= c / d;
                    ll t = m2 / d;
                    x = (x % t + t) % t;
                    modNum *= t;
                    leftNum = (leftNum + x * m1) % modNum;
                }
                if(f)   puts("NO");
                else    printf("%lld\n", leftNum);
            }
        }else puts("NO");
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值