首页 > 代码库 > [BZOJ4026]dC Loves Number Theory

[BZOJ4026]dC Loves Number Theory

[BZOJ4026]dC Loves Number Theory

试题描述

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 飞,害得我差点调了一年。。。

[BZOJ4026]dC Loves Number Theory