【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;
}

 

 

posted @ 2017-10-13 09:30  GXZlegend  阅读(416)  评论(0编辑  收藏  举报