试题描述
dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯竭的水题资源。
给定一个长度为 n的正整数序列A,有q次询问,每次询问一段区间内所有元素乘积的φ(φ(n)代表1~n 中与n互质的数的个数) 。由于答案可能很大,所以请对答案 mod 10^6 + 777。 (本题强制在线,所有询问操作的l,r都需要 xor上一次询问的答案 lastans,初始时,lastans = 0)
输入
第一行,两个正整数,N,Q,表示序列的长度和询问的个数。
第二行有N 个正整数,第i个表示Ai.
下面Q行,每行两个正整数,l r,表示询问[l ^ lastans,r ^ lastans]内所有元素乘积的φ
输出
Q行,对于每个询问输出一个整数。
输入示例
5 10 3 7 10 10 5 3 4 42 44 241 242 14 9 1201 1201 0 6 245 245 7 7 6 1 1203 1203
输出示例
40 240 12 1200 2 240 4 4 1200 4
数据规模及约定
1 <= N <= 50000
1 <= Q <= 100000
1 <= Ai <= 10^6
题解
把原序列中每个数 Ai 拆成不超过 logAi 个质因数,然后思路基本和上一题一样,只不过维护的东西换了(提示:想想怎么求 φ)。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); } return x * f; } #define maxn 800010 #define maxnode 12800010 #define maxp 1000010 #define maxlog 20 #define MOD 1000777 #define LL long long int A[maxn], nn, pos[maxn], lstp[maxp], Mulsum[maxn], inv[MOD]; void gcd(int a, int b, int& x, int& y) { if(!b){ x = 1; y = 0; return ; } gcd(b, a % b, y, x); y -= a / b * x; return ; } int Inv(int a) { int x, y; gcd(a, MOD, x, y); return (x % MOD + MOD) % MOD; } int prime[maxp], cnt, val[maxp], nxt[maxp], tval[maxlog]; bool vis[maxp]; void prime_table() { for(int i = 2; i <= maxp - 10; i++) { if(!vis[i]) prime[++cnt] = i, val[i] = prime[cnt], nxt[i] = i; for(int j = 1; j <= cnt && i * prime[j] <= maxp - 10; j++) { vis[i*prime[j]] = 1; val[i*prime[j]] = prime[j]; nxt[i*prime[j]] = i; if(i % prime[j] == 0) break; } } return ; } int ToT, rt[maxn], Mul[maxnode], lc[maxnode], rc[maxnode]; void update(int& y, int x, int l, int r, int p, int v) { if(v > 1) Mul[y = ++ToT] = ((LL)Mul[x] * (v - 1) % MOD) * inv[v] % MOD; else Mul[y = ++ToT] = Mul[x]; // printf("%d %d %d: %d(%d %d %d)\n", y, l, r, Mul[y], x, v, inv[v]); if(l == r) return ; int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x]; if(p <= mid) update(lc[y], lc[x], l, mid, p, v); else update(rc[y], rc[x], mid + 1, r, p, v); return ; } LL query(int o, int l, int r, int qr) { if(!o) return 1; if(r <= qr) return Mul[o]; int mid = l + r >> 1; LL ans = query(lc[o], l, mid, qr); if(qr > mid) (ans *= query(rc[o], mid + 1, r, qr)) %= MOD; return ans; } int ANS[maxn], len; char Out[maxn]; int main() { // freopen("data.in", "r", stdin); // freopen("data.out", "w", stdout); for(int i = 0; i < MOD; i++) inv[i] = Inv(i); prime_table(); int n = read(), q = read(); Mulsum[0] = 1; for(int i = 1; i <= n; i++) { int tmp = read(); Mulsum[i] = (LL)Mulsum[i-1] * tmp % MOD; pos[i] = nn + 1; if(tmp == 1) A[++nn] = 1; else { int j = tmp, tc = 0; for(; nxt[j] != j; j = nxt[j]) tval[++tc] = val[j]; tval[++tc] = val[j]; tc = unique(tval + 1, tval + tc + 1) - tval - 1; for(j = 1; j <= tc; j++) A[++nn] = tval[j]; } } pos[n+1] = nn + 1; n = nn; Mul[0] = 1; for(int i = 1; i <= n; i++) { update(rt[i], rt[i-1], 0, n, lstp[A[i]], A[i]); lstp[A[i]] = i; } /*printf("n: %d\n", n); for(int i = 1; i <= n; i++) printf("%d%c", A[i], i < n ? ‘ ‘ : ‘\n‘); for(int i = 1; i <= n; i++) printf("%d ", pos[i]); putchar(‘\n‘);*/ int lst = 0; for(int i = 1; i <= q; i++) { int ql = read() ^ lst, qr = read() ^ lst; int l = pos[ql], r = pos[qr+1] - 1; int Multi = (LL)Mulsum[qr] * inv[Mulsum[ql-1]] % MOD; // printf("Multi: %d %d %d %d %d\n", Multi, rt[r], rt[l-1], query(rt[r], 0, n, l - 1), query(rt[l-1], 0, n, l - 1)); lst = (query(rt[r], 0, n, l - 1) * inv[query(rt[l-1],0,n,l-1)] % MOD) * Multi % MOD; ANS[i] = lst; // lst = 0; } int num[10], cntn; for(int i = 1; i <= q; i++) { int tmp = ANS[i]; // printf("%d\n", ANS[i]); if(!tmp) Out[len++] = ‘0‘; cntn = 0; while(tmp) num[++cntn] = tmp % 10, tmp /= 10; for(int j = cntn; j; j--) Out[len++] = num[j] + ‘0‘; if(i < q) Out[len++] = ‘\n‘; else Out[len++] = ‘\0‘; } puts(Out); return 0; }
不知为何把求逆元的部分都改成 long long 交到大视野上就 T 飞,害得我差点调了一年。。。
时间: 2024-10-28 04:25:34