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;
}
posted @ 2023-05-30 21:06  zifanwang  阅读(4)  评论(0编辑  收藏  举报  来源