【bzoj3813】奇数国 线段树
题目描述
给出一个长度为n的序列,每个数都可以由前60个质数的乘积表示,初始每个数都是3。支持两种操作:(1)修改一个数 (2)查询一段区间内所有数的乘积的欧拉函数值模19961993。
输入
第一行一个整数x表示领袖清点和变动存款的总次数。
接下来x行,每行3个整数ai,bi,ci。ai为0时表示该条记录是清点计划,领袖会清点bi到ci的银行存款,你需要对该条记录计算出GFS想要的答案。ai为1时表示该条记录是存款变动,你要把银行bi的存款改为ci,不需要对该记录进行计算。
输出
输出若干行,每行一个数,表示那些年的答案。
样例输入
6
013
115
013
117
013
023
样例输出
18
24
36
6
题解
线段树
考虑到$\varphi$的求法:$\varphi(n)=n\sum\limits_{prime(p)\& p|n}\frac{p-1}p$。所以需要维护的就是区间乘积和区间所有出现过的质数。
由于所有数都可以由前60个质数表示,因此可以维护乘积中每个质数是否出现。使用二进制位运算即可。
最后对于每个质因子计算并求出答案。
时间复杂度$O(60m+m\log n)$。
#include <cstdio> #define N 100010 #define mod 19961993 #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 typedef long long ll; const int n = 100000; int p[60] , inv[60]; struct data { ll w , v; data() {} data(int x) { int i; w = x , v = 0; for(i = 0 ; i < 60 ; i ++ ) if(x % p[i] == 0) v |= (1ll << i); } data operator+(const data &a)const { data ans; ans.w = w * a.w % mod , ans.v = v | a.v; return ans; } }a[N << 2]; inline void pushup(int x) { a[x] = a[x << 1] + a[x << 1 | 1]; } void build(int l , int r , int x) { if(l == r) { a[x] = data(3); return; } int mid = (l + r) >> 1; build(lson) , build(rson); pushup(x); } void update(int p , int v , int l , int r , int x) { if(l == r) { a[x] = data(v); return; } int mid = (l + r) >> 1; if(p <= mid) update(p , v , lson); else update(p , v , rson); pushup(x); } data query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return a[x]; int mid = (l + r) >> 1; if(e <= mid) return query(b , e , lson); else if(b > mid) return query(b , e , rson); else return query(b , e , lson) + query(b , e , rson); } inline ll pow(ll x , int y) { ll ans = 1; while(y) { if(y & 1) ans = ans * x % mod; x = x * x % mod , y >>= 1; } return ans; } inline bool judge(ll x) { ll i; for(i = 2 ; i * i <= x ; i ++ ) if(x % i == 0) return 0; return 1; } inline void init() { ll i; int tot = 0; for(i = 2 ; tot < 60 ; i ++ ) if(judge(i)) p[tot] = i , inv[tot] = pow(p[tot] , mod - 2) , tot ++ ; } int main() { init(); int m , i , x , y , z; data t; scanf("%d" , &m); build(1 , n , 1); while(m -- ) { scanf("%d%d%d" , &x , &y , &z); if(x) update(y , z , 1 , n , 1); else { t = query(y , z , 1 , n , 1); for(i = 0 ; i < 60 ; i ++ ) if(t.v & (1ll << i)) t.w = t.w * (p[i] - 1) % mod * inv[i] % mod; printf("%lld\n" , t.w); } } return 0; }