[十二省联考2019]骗分过样例
传送门
这道题是个毒瘤题,花费了我\(\text{1day}\)独立解决\(16\)个子任务。下面步入正题。
subtask 1-3:1_998244353
这个观察数据不难得出要求\(19^x\bmod 998244353\),直接搞即可。注意到可能\(x\)非常大,根据费马小定理\(x^{P-1} \equiv 1 \pmod P\),我们需要读入取模。
subtask 4:1?
观察数据和提示告诉我们:仍然求\(19^x\),只不过模数不知道。发现输出文件的最大值在\(10^6\)左右,我们拿第一个输入直接爆搜检验,最后能找出来\(P=1145141\)。
subtask 5:1?+
这个是前一个的加强版,发现模数在\(5\times 10^{18}\)左右,这个不好暴力了。怎么办呢?我把输入的数排了个序,发现有两组输入的\(x\)之差为\(2\),于是我找到这两组对应的输出,得到了:\(19^{264708066}\equiv 1996649514996338529 \pmod P\)和\(19^{264708068}\equiv 1589589654696467295 \pmod P\)。也就是说上面的式子乘上\(19^2\)再取模就能得到下面的数字,于是我们得到了:\(1996649514996338529\times 19^2 \equiv 1589589654696467295 \pmod P\)。然后改写这个式子:\(1996649514996338529\times 361-nP=1589589654696467295\),把常数移到右边,发现在\(long\ long\)范围内无法算出,我用\(long\ double\)算出了近似值。然后\(P\)一定是这个数的一个因子。发现\(n\)在\(100\)到\(200\)左右,我就暴力试除,考虑到精度又将\(\pm 1000\)的模数用第一组输入输出判断了一下,最后找到了模数\(P=5211600617818708273\)。
subtask 6-7:1wa_998244353
发现并不是求\(19^x \bmod 998244353\)了,换成了用\(int\)一步一步直接乘再取模,忽视溢出等问题。代码如下。
x = (int)(x * 19) % 998244353;
第\(6\)个点直接顺序求解即可。第\(7\)个点恐怕不太行,\(x\)太大了。我一开始想改写快速幂来求解,发现行不通。正当我一筹莫展的时候,我让第\(6\)个点多跑到\(10^6\)组,发现了循环节。就是从\(x=55246\)开始,答案每过\(45699\)个数循环一次。这让我想起了\(\text{Pollard-Rho}\)算法的\(\rho\)。当然与那个算法没有关系,这里直接用上述性质即可。
subtask 14-16:2g && 2g+
当时我在做完前面\(6\)个\(\text{subtask}\)后紧接着做的。前面两个点,每次询问给你三个数\(l\),\(r\),\(P\),要求\(l\)到\(r\)在模\(P\)下的原根。对于第\(14\)个点,\(P=998244353\)时,\(\varphi(P)=P-1=998244352=2^{23}\times 7\times 17\),因为不同的质因子只有\(3\)个,所以可以直接试除判断是否为原根。
对于第\(15\)个点,\(P=13123111\),\(\varphi(P)=13123110=2×3×5×7×11×13×19×23\),而且判断的数字多达\(10^7\)个,试除肯定会\(\text{T}\)。我们可以利用其中一个原根把其他的原根遍历出来。在这里\(g\)取\(6\),因为\(g^t\)遍历所有\(\varphi(P)\)个与\(P\)互质的数,而当且仅当\(t\)与\(\varphi(P)\)互质的时候\(g^t\)也是原根。于是我们用\(\varphi(P)\)的质因子对\(t\)进行取模判断。遍历完后原根就找全了。
对于第\(16\)个点,最后一组询问未知模数,根据数据给的原根我们反求模数。提示说在\(10^9\)到\(2\times 10^9\)之间且是个质数,我们一个一个找,然后借助已有的原根通过试除法判断这个质数可不可行。\(\forall g\),如果\(g^\frac{P-1}{2}\not\equiv 1 \pmod P\),则\(P\)很可能是我们要找的模数。我的电脑跑了大约\(5min\)找到了一个数\(P=1515343657\),然后检验发现是正确的。用它搜原根与用\(P=998244353\)的方法一样。于是就解决了。
subtask 8-10:2p
要求我们判断\(l\)到\(r\)内每个数是不是质数。小范围的可以用线性筛,范围稍微大点的可能可以筛一部分数然后用这部分数来筛\(l\)到\(r\)。我直接上了\(\text{Miller-Rabin}\),这个算法可以在\(\log n\)的时间内测试一个数是不是质数,正确率为\(1-(\frac{1}{4})^s\),\(s\)为测试次数。这题貌似选取两三个数就可以了,这样常数小全能过。
subtask 11-13:2u
这里让我们筛出\(l\)到\(r\)的莫比乌斯函数。
同上面一样,我们可以小范围地筛出来\(\mu\)过掉前两个点。当时我没有这么想,用了\(\text{Pollard-Rho}\)来暴力分解然后筛出前\(10^6\)个莫比乌斯函数,发现第二个点都过不掉。怎么办呢?最大的数为\(10^{18}\),我想到如果筛出\(10^6\)以内的质数然后用这些质数来筛这些数,剩下的数的素因子一定\(\geq 10^6\),所以剩下的数最多只能有两个素因子,也就是以下三种情况:
一、剩下的数是质数,用\(\text{Miller-Rabin}\)判一下,这个时候会使\(\mu\)乘上\(-1\);
二、剩下的数是两个不同的质数的乘积;负负得正,这个时候不会对\(\mu\)产生贡献;
三、剩下的数是一个质数的平方。这个时候\(\mu\)为\(0\)。
用小于\(10^6\)的因子去筛这些数,并且维护\(\mu\),然后通过上面的三种情况分类讨论即可求解。
但要注意要去掉含有平方因子的数。最后这道题就解完了。
\\ AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rep(i, a, b) for (register int i = a, end = b; i <= end; i++)
#define repd(i, a, b) for (register int i = a, end = b; i >= end; i--)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)
#define INF (1<<30)
#define pb push_back
#define mp(a, b) make_pair(a, b)
#define fst first
#define snd second
#define pii pair<int, int>
char s[15];
namespace _998244353 {
int N;
ull P;
ull v;
inline void inc(ull &a, ull b, ull p) {
a += b;
if (a >= p) a -= p;
}
ull Mult(ull a, ull b, ull p) {
ull res = 0;
for (ull k = a; b; inc(k, k, p), b >>= 1)
if (b & 1) inc(res, k, p);
return res;
}
ull qpow(ull a, ull b, ull p) {
ull res = 1;
for (register ull k = a; b; k = Mult(k, k, p), b >>= 1)
if (b & 1) res = Mult(res, k, p);
return res;
}
inline ull read() {
ull w = 0; char c;
while (!isdigit(c = getchar())) ;
while (isdigit(c)) w = ((w << 3) + (w << 1) + (c ^ 48)) % (P-1), c = getchar();
return w;
}
void main(ull orz) {
P = orz;
scanf("%d", &N);
rep(i, 1, N) {
v = read();
printf("%llu\n", qpow(19, v, P));
}
}
}
namespace WA {
int N;
int ans[55246+45699+5];
void main() {
scanf("%d", &N);
ans[0] = 1;
rep(i, 1, 55246+45699) {
ans[i] = (int)(ans[i-1]*19)%998244353;
}
rep(i, 1, N) {
ll val;
scanf("%lld", &val);
if (val <= 55246+45699) printf("%d\n", ans[val]);
else printf("%d\n", ans[(val-55246)%45699+55246]);
}
}
}
namespace GG {
int qpow(int a, int b, int p) {
int res = 1;
for (register int k = a; b; k = (ll)k*k%p, b >>= 1)
if (b & 1) res = (ll)res * k % p;
return res;
}
void run1(int l, int r, int p) {
if (p == 998244353) {
rep(i, l, r) if (qpow(i, 499122176, 998244353) != 1 && qpow(i, 142606336, 998244353) != 1 && qpow(i, 58720256, 998244353) != 1) printf("g"); else printf(".");
} else {
rep(i, l, r)
if (qpow(i, 757671828, 1515343657) != 1 && qpow(i, 505114552, 1515343657) != 1 &&
qpow(i, 378552, 1515343657) != 1 && qpow(i, 96072, 1515343657) != 1) printf("g"); else printf(".");
}
puts("");
}
int st[13123120];
void run2(int p) {
memset(st, 0, sizeof(st));
int g = 6, cnt = 0;
do {
cnt++;
if (cnt % 2 && cnt % 3 && cnt % 5 && cnt % 7 && cnt % 11 && cnt % 13 && cnt % 19 && cnt % 23) st[g] = 1;
g = g*6%p;
} while (g != 6);
rep(i, 1, 13123110) if (st[i]) printf("g"); else printf(".");
puts("");
}
}
namespace PP {
inline ll Mult(ll a, ll b, ll p) {
ll c = (ll)a*b - (ll)((ull)((long double)a*b/p)*p);
return c < 0 ? c+p : ((ull)c >= (ull)p ? c-p : c);
}
ll qpow(ll a, ll b, ll p) {
ll res = 1;
for (register ll k = a; b; k = Mult(k, k, p), b >>= 1)
if (b & 1) res = Mult(res, k, p);
return res;
}
int test[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
bool MR(ll P, int cnt = 10) {
ll s = P-1; int t = 0;
while (!(s & 1)) s >>= 1, t++;
rep(i, 0, cnt-1) {
if (P == test[i]) return true;
if (test[i] > P) return false;
ll a = qpow(test[i], s, P), nxt;
rep(x, 1, t) {
nxt = Mult(a, a, P);
if (nxt == 1 && a != 1 && a != P-1) return false;
a = nxt;
if (a == 1) break;
}
if (a != 1) return false;
}
return true;
}
int N;
void main() {
scanf("%d", &N);
while (N--) {
ll l, r;
scanf("%lld%lld", &l, &r);
while (l <= r) {
if (MR(l)) printf("p"); else printf(".");
l++;
}
puts("");
}
}
}
namespace UU {
int N;
int check[1000005], p[100000];
void init() {
memset(check, 0, sizeof(check));
p[0] = 0;
rep(i, 2, 1000000) {
if (!check[i]) p[++p[0]] = i;
for (register int j = 1; j <= p[0] && i*p[j] <= 1000000; j++) {
check[i*p[j]] = 1;
if (!(i % p[j])) break;
}
}
}
ll frac[1000001], mu[1000001];
bool issqr(ll x) {
ll v = sqrt(x);
if (v*v == x || (v-1)*(v-1)==x || (v+1)*(v+1)==x) return true;
return false;
}
#define cc(x) ((x) == 0 ? '0' : ((x) < 0 ? '-' : '+'))
void main() {
init();
scanf("%d", &N);
while (N--) {
ll l, r;
scanf("%lld%lld", &l, &r);
rep(i, 0, r-l) mu[i] = frac[i] = 1;
rep(i, 1, p[0]) {
ll x = 1ll*p[i]*p[i], st = l-(l-1)%x-1+x;
while (st <= r) {
mu[st-l] = 0; frac[st-l] = st; st += x;
}
x = p[i], st = l-(l-1)%x-1+x;
while (st <= r) {
mu[st-l] = -mu[st-l];
if (frac[st-l] != st) frac[st-l] *= x;
st += x;
}
}
for (register ll i = l; i <= r; i++) {
ll val = i/frac[i-l];
if (val == 1) printf("%c", cc(mu[i-l]));
else if (PP::MR(val, 2)) printf("%c", cc(-mu[i-l]));
else if (issqr(val)) printf("0");
else printf("%c", cc(mu[i-l]));
}
puts("");
}
}
}
int main() {
srand(time(0));
scanf("%s", s);
if (s[2] == '9') _998244353::main(998244353);
if (s[1] == '?') {
if (s[2] == '+') {
_998244353::main(5211600617818708273ll);
} else {
_998244353::main(1145141);
}
}
if (s[1] == 'w') {
WA::main();
}
if (s[1] == 'g') {
int l, r, p, N;
if (s[2] == '?') {
scanf("%d", &N);
while (N--) {
scanf("%d%d", &l, &r);
if (N) scanf("%d", &p); else p = 1515343657;
GG::run1(l, r, p);
}
} else {
scanf("%d", &N);
while (N--) {
scanf("%d%d%d", &l, &r, &p);
if (p == 998244353) GG::run1(l, r, p);
else GG::run2(p);
}
}
}
if (s[1] == 'p') {
PP::main();
}
if (s[1] == 'u') {
UU::main();
}
return 0;
}