题解 算术

传送门

我的思路引入是全同态加密

考虑一个全同态哈希使得 \(h(m)^k\equiv h(m^k)\)
这个 \(h(x)\) 怎么构造呢?发现 \(h(x)=x \bmod p\) 就满足这个性质所以……
损失一些正确率将 \(h(m)\) 的值域降到可以枚举的范围
于是可以枚举 check 是否存在这样一个 \(h(m)\)
其实这样在做的事情就是 check \(n\bmod p\) 是否存在 \(k\) 次剩余但我赛时还不知道 \(k\) 次剩余是什么

发现当 \(k\) 为质数时性质使

\[\{0, 1, \cdots p-1\}\equiv\{0^k, 1^k, \cdots,(p-1)^k\}\pmod p \]

即每个数在 \(\bmod p\) 意义下均有 \(k\) 次剩余
考虑原根转化,即为

\[\{0, 1, \cdots p-1\}\equiv\{0\times k, 1\times k, \cdots,(p-1)\times k\}\pmod{\varphi(p)} \]

发现每个数 \(i\) 出现了 \(\gcd(k, \varphi(p))\)
所以每个数在 \(\bmod p\) 意义下均有 \(k\) 次剩余的条件是 \(\gcd(k, \varphi(p))=1\)
那么我们希望让这个 gcd 尽量大
所以我们选用 \(p=ak+1\and p\in \tt prime\)

一些关于 二次剩余的判别准则 的东西
那么我们可以推广到 \(n\) 存在 \(k\) 次剩余存在的条件是 \(n^{\frac{p-1}{k}}\equiv 1\pmod p\)
于是就可以判断了

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define pb push_back
#define ll long long
#define int128 __int128
//#define int long long

int n;
ll mod;
bool ans[21];
char s[21][N];
int len[21], k[21];
int pri[]={11, 13, 17, 19, 23, 97, 131, 13131, 1837383}, tot=9;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}

namespace task1{
	inline int128 qpow(int128 a, int128 b) {int128 ans=1; for (; b; a=a*a,b>>=1) if (b&1) ans=ans*a; return ans;}
	void solve() {
		for (int i=1; i<=n; ++i) {
			ll m=0;
			for (int j=1; j<=len[i]; ++j) m=m*10+s[i][j]-'0';
			if (m==1) {puts("Y"); continue;}
			if (k[i]>65) {puts("N"); continue;}
			int128 t=pow(m, 1.0L/k[i]);
			if (qpow(t-1, k[i])==m || qpow(t, k[i])==m || qpow(t+1, k[i])==m) puts("Y");
			else puts("N");
		}
	}
}

namespace task2{
	void solve() {
		for (int i=1; i<=n; ++i) ans[i]=1;
		for (int now=0; now<tot; ++now) {
			mod=pri[now];
			for (int i=1; i<=n; ++i) if (ans[i]) {
				ll h=0;
				for (int j=1; j<=len[i]; ++j) h=(h*10+s[i][j]-'0')%mod;
				for (int j=0; j<mod; ++j) if (qpow(j, k[i])==h) goto jump;
				ans[i]=0;
				jump: ;
			}
		}
		for (int i=1; i<=n; ++i) puts(ans[i]?"Y":"N");
	}
}

namespace task{
	bool ispri(int i) {
		for (int j=2; j*j<=i; ++j)
			if (i%j==0) return 0;
		return 1;
	}
	void solve() {
		for (int i=1; i<=n; ++i) ans[i]=1;
		for (int now=5; clock()<200000; ++now) {
			for (int i=1; i<=n&&clock()<200000; ++i) if (ans[i]) {
				mod=now*k[i]+1;
				if (!ispri(mod)) continue;
				ll h=0;
				for (int j=1; j<=len[i]; ++j) h=(h*10+s[i][j]-'0')%mod;
				if (!h) continue;
				// for (int j=0; j<mod; ++j) if (qpow(j, k[i])==h) goto jump;
				if (qpow(h, now)==1) goto jump;
				ans[i]=0;
				jump: ;
			}
		}
		for (int i=1; i<=n; ++i) puts(ans[i]?"Y":"N");
	}
}

signed main()
{
	freopen("math.in", "r", stdin);
	freopen("math.out", "w", stdout);

	scanf("%d", &n);
	int max_len=0;
	for (int i=1; i<=n; ++i) {
		scanf("%s%d", s[i]+1, &k[i]);
		max_len=max(max_len, len[i]=strlen(s[i]+1));
	}
	task::solve();

	return 0;
}
posted @ 2022-03-14 21:38  Administrator-09  阅读(1)  评论(0编辑  收藏  举报