P4435 [COCI2017-2018#2] Garaža 题解
这题的题号比较神奇,时隔这么久还是写一篇题解吧。
考虑用线段树进行维护,合并时加上当前区间内过 \(mid\) 的区间个数。
对于每个线段树节点,维护这个节点的区间 \([L,R]\) 中所有以 \(L\) 开头和以 \(R\) 结尾的区间的 \(\gcd\),不同的 \(\gcd\) 的个数是 \(\log\) 级别的,只需要记录每段的值和开始的位置,然后合并时两次扫描线统计个数即可。
时间复杂度:\(O(n\log^2n)\)。
代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 100003
#define md 1000000007
#define pb push_back
#define mkp make_pair
#define ld long double
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
struct node{
ll x;
int l,r;
int t1,p1[35],d1[35];
int t2,p2[35],d2[35];
}t[mxn<<2];
int n,q,a[mxn];
inline int gcd(int x,int y){
while(y^=x^=y^=x%=y);
return x;
}
node operator+(node x,node y){
node s;
s.l=x.l,s.r=y.r;
s.x=x.x+y.x;
s.t1=x.t1;
rep(i,1,x.t1)s.p1[i]=x.p1[i],s.d1[i]=x.d1[i];
int d=x.d1[x.t1];
rep(i,1,y.t1){
int ls=d;d=gcd(d,y.d1[i]);
if(d!=ls)s.p1[++s.t1]=y.p1[i],s.d1[s.t1]=d;
}
s.t2=y.t2;
rep(i,1,y.t2)s.p2[i]=y.p2[i],s.d2[i]=y.d2[i];
d=y.d2[y.t2];
rep(i,1,x.t2){
int ls=d;d=gcd(d,x.d2[i]);
if(d!=ls)s.p2[++s.t2]=x.p2[i],s.d2[s.t2]=d;
}
int p=1;
drep(i,y.t1,1){
while(p<x.t2&&gcd(y.d1[i],x.d2[p+1])>1)p++;
if(gcd(y.d1[i],x.d2[p])>1)s.x+=(ll)(x.r-(p==x.t2?x.l-1:x.p2[p+1]))*((i==y.t1?s.r+1:y.p1[i+1])-y.p1[i]);
}
return s;
}
void build(int p,int l,int r){
if(l==r){
t[p].l=t[p].r=l;
t[p].x=a[l]>1;
t[p].t1=t[p].t2=1;
t[p].p1[1]=t[p].p2[1]=l;
t[p].d1[1]=t[p].d2[1]=a[l];
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
t[p]=t[p<<1]+t[p<<1|1];
}
void change(int p,int x,int l,int r){
if(l==r){
t[p].x=a[l]>1;
t[p].d1[1]=t[p].d2[1]=a[l];
return;
}
int mid=(l+r)>>1;
if(x<=mid)change(p<<1,x,l,mid);
else change(p<<1|1,x,mid+1,r);
t[p]=t[p<<1]+t[p<<1|1];
}
node ask(int p,int l,int r,int L,int R){
if(l<=L&&R<=r)return t[p];
int mid=(L+R)>>1;
if(l<=mid&&r>mid)return ask(p<<1,l,r,L,mid)+ask(p<<1|1,l,r,mid+1,R);
if(l<=mid)return ask(p<<1,l,r,L,mid);
return ask(p<<1|1,l,r,mid+1,R);
}
signed main(){
scanf("%d%d",&n,&q);
rep(i,1,n)scanf("%d",&a[i]);
build(1,1,n);
int op,x,y;
while(q--){
scanf("%d%d%d",&op,&x,&y);
if(op==1){
a[x]=y;
change(1,x,1,n);
}else{
printf("%lld\n",ask(1,x,y,1,n).x);
}
}
return 0;
}