洛谷 P3987 我永远喜欢珂朵莉~(平衡树)

题目链接

题意:给出一个序列 \(a(n)\),需要支持以下两个操作
1 l r x 将 \([l,r]\) 中 \(x\) 的倍数除以 \(x\)
2 l r 求 \([l,r]\) 的和。
\(1 \leq n \leq 10^5\),\(1 \leq a_i \leq 5 \times 10^5\)

真·卡常毒瘤题,加强版还没过
不难发现每个数最多除 \(\log(500000)\) 次,所以总次数不会超过 \(2 \times 10^6\),至于求和,可以用树状数组维护。
所以这道题的难点就变为了怎么找出 \(x\) 的倍数。
我们对于每个 \(x\) 建一棵平衡树,里面插入所有 \(a_i\) 是 \(x\) 的倍数的 \(i\)。
对于一次 \(1\) 操作,只需将第 \(x\) 棵树的 \([l,r]\) 截出来,将数中的 \(x\) 的倍数都除以 \(x\),如果除完之后不是 \(x\) 的倍数,就将它从这个平衡树删除。
就 好 了
代码:(大概要开 O2 才能过)

#include <bits/stdc++.h>
using namespace std;
#define fz(i,a,b)	for(register int i=a;i<=b;i++)
#define fd(i,a,b)	for(register int i=a;i>=b;i--)
//#define int long long
typedef long long ll;
inline int read(){
	int x=0,neg=1;char c=getchar();
	while(!isdigit(c)){
		if(c=='-')	neg=-1;
		c=getchar();
	}
	while(isdigit(c))	x=x*10+c-'0',c=getchar();
	return x*neg;
}
inline void print(ll x){
	(x<=9)?(putchar(x+'0')):(print(x/10),putchar(x%10+'0'));
}
int n=read(),m=read(),a[100005];
int dels[500005],dels_cnt;
ll bit[100005];
inline void add(int x,int y){
	for(int i=x;i<=n;i+=(i&(-i)))	bit[i]+=y;
}
inline ll query(int x){
	ll sum=0;
	for(int i=x;i;i-=(i&(-i)))	sum+=bit[i];
	return sum;
}
struct node{
	int ch[2],ind,key;
};
int qt[100005],qts=0;
node s[20000005];
int rt[500005];
int cnt=0;
vector<int> f[500005];
inline int newnode(int ind){
	s[++cnt].ind=ind;
	s[cnt].key=rand();
	return cnt;
}
inline void build(int &k,int l,int r){
	int mid=(l+r)>>1;
	k=newnode(qt[mid]);
	if(l^mid)	build(s[k].ch[0],l,mid-1);
	if(mid^r)	build(s[k].ch[1],mid+1,r);
}
inline void split(int k,int val,int &a,int &b){
	if(!k){a=b=0;return;}
	(val<s[k].ind)?(b=k,split(s[k].ch[0],val,a,s[k].ch[0])):(a=k,split(s[k].ch[1],val,s[k].ch[1],b));
}
inline int merge(int a,int b){
	if(!a||!b)	return a+b;
	return (s[a].key>s[b].key)?(s[a].ch[1]=merge(s[a].ch[1],b),a):(s[b].ch[0]=merge(a,s[b].ch[0]),b);
}
inline void del(int ind,int x){
	int k1,k2,k3;
	split(rt[x],ind-1,k1,k2);
	split(k2,ind,k2,k3);
	rt[x]=merge(k1,k3);
}
inline void dfs(int k,int x){
	if(!k)	return;
	dfs(s[k].ch[0],x);
	if(a[s[k].ind]%x==0)	add(s[k].ind,-a[s[k].ind]),a[s[k].ind]/=x,add(s[k].ind,a[s[k].ind]);
	if(a[s[k].ind]%x!=0)	dels[++dels_cnt]=s[k].ind;
	dfs(s[k].ch[1],x);
}
inline void solve(int x,int l,int r){
	if(x==1)	return;
	int k1,k2,k3;
	split(rt[x],l-1,k1,k2);
	split(k2,r,k2,k3);
	dels_cnt=0;
	dfs(k2,x);
	rt[x]=merge(merge(k1,k2),k3);
	fz(i,1,dels_cnt)	del(dels[i],x);
}
signed main(){
	srand(time(0));
	fz(i,1,n)	a[i]=read(),add(i,a[i]);
	fz(i,1,n){
		if(!a[i])	continue;
		for(int j=1;j*j<=a[i];j++){
			if(a[i]%j==0){
				f[j].push_back(i);
				if(a[i]/j!=j)	f[a[i]/j].push_back(i);
			}
		}
	}
	fz(i,1,500000){
		if(f[i].size()){
			qts=0;
			for(int j=0;j<f[i].size();j++){
				qt[++qts]=f[i][j];
			}
			build(rt[i],1,qts);
		}
	}
	ll ans=0;
	while(m--){
		int opt=read();
		if(opt&1){
			int l=read(),r=read(),x=read();
			//l^=ans;r^=ans;x^=ans;
			solve(x,l,r);
		}
		else{
			int l=read(),r=read();
			//l^=ans;r^=ans;
			ans=query(r)-query(l-1);
			print(ans);putchar('\n');
		}
	}
}
上一篇:GTD实践2周年后一些体会


下一篇:Query on a tree VI [SP16549]