ICPC2019 西安邀请赛 I.Cracking Password

ICPC2019 西安邀请赛 I.Cracking Password

题目链接

https://nanti.jisuanke.com/t/39276

题目大意

给你一个长度为 $n$ 的数列 $b$,问是否存在一对 $a$ 和 $p$ 使得这个数列是数列 $a_i = a ^ i \mod p$ 的一段连续子序列。要求 $p$ 是质数。

题目分析

对于给你的数列 $b$ ,那么对于任意连续的三个数 $x, y, z$ 那么有 $y = ax, z=a^2x$,所以有 $xz = y ^ 2$ 。那么 $y ^ 2 – xz$ 一定是 $p$ 的倍数。所以找到一组 $y ^ 2 – xz \neq 0$ 的连续的三个数。如果全是 $0$ 的话那么就是 $unsure$ 。找到一组之后,将这个差值分解质因数,对于每个质数,可以通过前两个数计算出 $a$ 。随后可以验证这组求出来的 $a$ 和 $p$ 是否满足题意。对于第一个数 $x$,它一定有 $a ^ t \mod p = x$ ,对于使用离散对数求解即可。注意如果所有的数相同,那么只有全是 $1$ 的时候才是 $unsure$ ,否则是 $error$。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>

using namespace std;

const int maxn = 1e5 + 5;

long long n;

long long b[maxn];
long long a, p;

int read(){
	int x;
	scanf("%d", &x);
	return x;
}

long long mul(long long x, long long y){
	long long ret = 0;
	long long k = x / 10000000ll;
	ret = (ret + 10000000ll * y % p * k % p) % p;
	x %= 10000000ll;
	ret = (ret + x * y) % p;
	return ret;
}

long long qpow(long long b, long long x){
	long long ret = 1;
	
	while(x){
		if(x & 1){
			ret = mul(ret, b) % p;
		}
		b = mul(b, b) % p;
		x >>= 1;
	}
	
	return ret;
}

long long dis_log(long long a, long long b){
	long long n = (long long)sqrt(p + .0) + 1;
	map <long long, long long> vals;
	
	long long an = 1;
	for(long long i=0;i<n;i++){
		an = mul(an, a);
	}
	
	for(long long i=1,cur=an;i<=n;i++){
		if(!vals.count(cur)){
			vals[cur] = i;
		}
		cur = mul(cur, an);
	}
	
	for(long long i=0,cur=b;i<=n;i++){
		if(vals.count(cur)){
			long long ans = vals[cur] * n - i;
			if(ans < p){
				return ans;
			}
		}
		cur = mul(cur, a);
	}
	
	return -1;
}

bool check(){
	int i, j;
	
	for(i=1;i<=n;i++){
		if(b[i] >= p)return false;
	}
	
	for(i=1;i<n;i++){
		if(a * b[i] % p != b[i + 1]){
			return false;
		}
	}
	
	if(dis_log(a, b[1]) == -1){
		return false;
	}
	
	return true;
}

bool solve(int x){
	long long i, j;
	long long y;
	long long t;
	
	y = abs(b[x - 1] * b[x + 1] - b[x] * b[x]);
	
	t = sqrt(y) + 1;
	
	for(p=2;p<=t;p++){
		if(y % p == 0){
			while(y % p == 0){
				y /= p;
			}
			if(b[x] % __gcd(b[x - 1], p))continue;
			a = b[x] * qpow(b[x - 1], p - 2) % p;
			if(check()){
				return true;
			}
		}
	}
	
	if(y > 1){
		p = y;
		while(y % p == 0){
			y /= p;
		}
		if(b[x] % __gcd(b[x - 1], p))return false;
		a = b[x] * qpow(b[x - 1], p - 2) % p;
		if(check()){
			return true;
		}
	}
	
	return false;
}

int main(){
	int i, j;
	int x = -1;
    bool flag = false;
	
	n = read();
	
	for(i=1;i<=n;i++){
		b[i] = read();
        if(i > 1)flag |= (b[i] != b[i - 1]);
	}
    
    if(n == 1){
        printf("unsure\n");
        return 0;
    }
	
	for(i=2;i<n;i++){
		if(b[i - 1] * b[i + 1] != b[i] * b[i]){
			x = i;
			break;
		}
	}
	
	if(x == -1){
		if(flag or b[1] == 1){
			printf("unsure\n");
		}else{
			printf("error\n");
		}
	}else{
		if(solve(x)){
			printf("%lld %lld\n", a, p);
		}else{
			printf("error\n");
		}
	}
	
	return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据