【bzoj3813】: 奇数国 数论-线段树-欧拉函数

时间:2023-03-08 17:02:21

【bzoj3813】: 奇数国

题意:给定一个序列,每个元素可以分解为最小的60个素数的形式。(x=p1^k1*p2^k2*......p60^k60)(p1=2,p2=3,…,p60=281)

支持单点修改,查询一段区间的积的欧拉函数 mod 19961993(是一个质数)。

线段树维护区间积x,bitset b[i]记录第i个素数是否存在。

预处理inv[i]=(p[i]-1)/p[i] mod 19961993

ans=x*inv[i] (b[i]==1)

 /* http://www.cnblogs.com/karl07/ */
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <bitset>
#define LL long long
using namespace std;
const int P=;
const int N=;
const int prime[]={,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,};
const int inv[]={,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,}; struct data{
bitset <> b;
LL x;
data (LL x=){
if (x==){
this->x=;
this->b.reset();
}else{
this->x=x;
for (int i=;i<;i++){
if (!(x%prime[i])) this->b[i]=;
}
}
}
};
data operator + (const data &a,const data &b){
data c;
c.b=a.b|b.b; c.x=a.x*b.x%P;
return c;
}
struct seg{
data d;
seg *ls,*rs;
} t[N*+]; seg *root,*NEW=t;
seg *new1(){ return ++NEW;} #define mid (ll+rr)/2
void build(seg *p,int ll,int rr){
if (ll==rr){
p->d=data();
}else{
build(p->ls=new1(),ll,mid);
build(p->rs=new1(),mid+,rr);
p->d=p->ls->d+p->rs->d;
}
} void modify(seg *p,int ll,int rr,int x,data d){
if (ll==rr){
p->d=d;
}else{
if (x<=mid) modify(p->ls,ll,mid,x,d);
if (x>mid) modify(p->rs,mid+,rr,x,d);
p->d=p->ls->d+p->rs->d;
}
} data query(seg *p,int ll,int rr,int l,int r){
data ans=data();
if (l<=ll && r>=rr){
ans=ans+p->d;
}else{
if (l<=mid) ans=ans+query(p->ls,ll,mid,l,r);
if (r>mid) ans=ans+query(p->rs,mid+,rr,l,r);
}
return ans;
} LL cal(data d){
LL ans=d.x;
for (int i=;i<;i++){
if (d.b[i]==){
ans=ans*inv[i]%P;
}
}
return ans;
} int n; int main(){
scanf("%d",&n);
build(root=new1(),,N);
for (int i=;i<=n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if (a==){
printf("%lld\n",cal(query(root,,N,b,c)));
}else{
modify(root,,N,b,data(c));
}
}
return ;
}