线段树学习笔记

普通线段树

CF242E XOR on Segment

题意:区间异或上一个数&区间求和

可以考虑拆位,然后每一个位都写一个线段树。线段树维护区间内部有多少个1,tag维护下面这个区间是否异或1(即01反转)。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,a[N],m;

bool bit(int s,int i) {return s&(1ll<<i);}

struct segment_tree {
	struct node {int cnt,tag;} t[N<<2];
	void build(int p,int l,int r,int k) {
		if(l==r) {t[p].cnt=bit(a[l],k); return;}
		build(p*2,l,(l+r)/2,k), build(p*2+1,(l+r)/2+1,r,k);
		t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
	}
	void pushdown(int p,int l,int r) {
		if(t[p].tag) {
			t[p*2].tag^=1, t[p*2+1].tag^=1;
			t[p*2].cnt=(l+r)/2-l+1-t[p*2].cnt;
			t[p*2+1].cnt=r-(l+r)/2-t[p*2+1].cnt;
			t[p].tag=0;
		}
	}
	void modify(int p,int x,int y,int l,int r) {
		int mid=(l+r)/2;
		if(x==l&&y==r) {t[p].cnt=r-l+1-t[p].cnt, t[p].tag^=1; return;}
		pushdown(p,l,r);
		if(y<=mid) modify(p*2,x,y,l,mid);
		else if(x>mid) modify(p*2+1,x,y,mid+1,r);
		else modify(p*2,x,mid,l,mid), modify(p*2+1,mid+1,y,mid+1,r);
		t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
	}
	int query(int p,int x,int y,int l,int r) {
		int mid=(l+r)/2;
		if(x==l&&y==r) return t[p].cnt;
		pushdown(p,l,r);
		if(y<=mid) return query(p*2,x,y,l,mid);
		else if(x>mid) return query(p*2+1,x,y,mid+1,r);
		else return query(p*2,x,mid,l,mid)+query(p*2+1,mid+1,y,mid+1,r);
	}
}st[21];

signed main() {
	n=read();
	rep(i,1,n) a[i]=read();
	rep(h,0,19) st[h].build(1,1,n,h);
	m=read();
	rep(i,1,m) {
		int opt=read(), l=read(), r=read();
		if(opt==1) {
			int ans=0;
			rep(h,0,19) ans+=st[h].query(1,l,r,1,n)*(1ll<<h);
			printf("%lld\n",ans);
		} else {
			int x=read();
			rep(h,0,19) if(bit(x,h)) st[h].modify(1,l,r,1,n);
		}
	}
	return 0;
}

CF620E New Year Tree

维护每个数有没有在区间出现过。由于数<=60,可以考虑直接状压。合并区间就直接or一下两个子区间即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
typedef long long ll;
const int N=4e5+9; 

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

struct edge {int to,nxt;} e[N*2]; int hd[N],tot;
void add(int u,int v) {e[++tot]=(edge){v,hd[u]};hd[u]=tot;}

int n,a[N],m,dt[N],st[N],ed[N],tick;

void dfs(int u,int fa) {
	dt[st[u]=++tick]=u;
	for(int i=hd[u],v;i;i=e[i].nxt) {
		if((v=e[i].to)==fa) continue;
		dfs(v,u);
	}
	ed[u]=tick;
}

struct node {int l,r,tag; ll s;} t[N<<2];
void build(int p,int l,int r) {
	t[p].l=l, t[p].r=r;
	if(l==r) {t[p].s=(1ll<<a[dt[l]]); return;}
	build(p*2,l,(l+r)/2), build(p*2+1,(l+r)/2+1,r);
	t[p].s=t[p*2].s|t[p*2+1].s;
}
void pushdown(int p) {
	t[p*2].tag=t[p*2+1].tag=t[p].tag;
	t[p*2].s=t[p*2+1].s=(1ll<<t[p].tag);
	t[p].tag=0;
}
void modify(int p,int x,int y,int c) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(l==x&&r==y) {t[p].s=(1ll<<c),t[p].tag=c; return;}
	if(t[p].tag) pushdown(p);
	if(y<=mid) modify(p*2,x,y,c);
	else if(x>mid) modify(p*2+1,x,y,c);
	else modify(p*2,x,mid,c), modify(p*2+1,mid+1,y,c);
	t[p].s=t[p*2].s|t[p*2+1].s;
}
ll query(int p,int x,int y) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(l==x&&y==r) return t[p].s;
	if(t[p].tag) pushdown(p);
	if(y<=mid) return query(p*2,x,y);
	else if(x>mid) return query(p*2+1,x,y);
	else return query(p*2,x,mid)|query(p*2+1,mid+1,y);
}

int main() {
	n=read(), m=read();
	rep(i,1,n) a[i]=read();
	rep(i,1,n-1) {
		int u=read(), v=read();
		add(u,v), add(v,u);
	}
	dfs(1,0);
	build(1,1,n);
	rep(i,1,m) {
		int opt=read(), u=read();
		if(opt==1) modify(1,st[u],ed[u],read());
		else {
			ll s=query(1,st[u],ed[u]); int ans=0;
			rep(h,0,60) ans+=(bool)(s&(1ll<<h));
			printf("%d\n",ans);
		}
	}
	return 0;
}

CF438D The Child and Sequence

取模很快就会膜小,所以和花神一题相似,我们暴力修改,并判断最大值是否<x即可。

CF431E Chemistry Experiment

可以二分+线段树 \(O(n\log^2n)\)。但由于线段树本来就是个二叉结构,所以考虑直接线段树上二分。首先我们要把所有准备加水的试管的液体量变一致。所以我们可以判断(已有的sz+左子树的sz)乘上mid再减去(已有的sum+左子树的sum)是否不小于x,如果是那么就走左边。否则走右边。

要动态开点。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,m,h[N],mx=1000000000;

int tot=1;
struct node {int ls,rs,sz,sum;} t[N*32];
void ins(int p,int l,int r,int x) {
	int mid=(l+r)/2;
	if(l==r) {t[p].sz++,t[p].sum+=x;return;}
	if(x<=mid) ins(t[p].ls?t[p].ls:t[p].ls=++tot,l,mid,x);
	else ins(t[p].rs?t[p].rs:t[p].rs=++tot,mid+1,r,x);
	t[p].sz=t[t[p].ls].sz+t[t[p].rs].sz,
	t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
}
void del(int p,int l,int r,int x) {
	int mid=(l+r)/2;
	if(l==r) {t[p].sz--,t[p].sum-=x;return;}
	if(x<=mid) del(t[p].ls,l,mid,x);
	else del(t[p].rs,mid+1,r,x);
	t[p].sz=t[t[p].ls].sz+t[t[p].rs].sz,
	t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
}
double query(int x) {
	int l=0,r=mx,p=1,ssz=0,ssum=0;
	while(l!=r) {
		double mid=(l+r)/2;
		if(t[p].ls&&(ssz+t[t[p].ls].sz)*(mid+0.9999)-ssum-t[t[p].ls].sum>=x) p=t[p].ls,r=(l+r)/2;
		else {
			if(t[p].ls) ssz+=t[t[p].ls].sz, ssum+=t[t[p].ls].sum;
			p=t[p].rs, l=(l+r)/2+1;
		}
	}
	return min(1.*(ssum+t[p].sum+x)/(ssz+t[p].sz),1.*(ssum+x)/ssz);
}

signed main() {
	//freopen("CF431E.in","r",stdin);
	//freopen("CF431E.out","w",stdout);
	n=read(), m=read();
	rep(i,1,n) ins(1,0,mx,h[i]=read());
	rep(i,1,m) {
		int opt=read(),p=read();
		if(opt==1) del(1,0,mx,h[p]), ins(1,0,mx,h[p]=read());
		else printf("%.5lf\n",query(p));
	}
	return 0;
}

CF240F TorCoder

首先重排后能成为回文串等价于出现奇数次数的字母不多于1个。对于区间 l,r,由于要最小字典序,所以我们把每个字母按顺序都在两边分别塞 \(cnt/2\) 个。对于那个奇数的,我们把它剩余的1个放在中间即可。对于每个字母,我们现在要求支持:区间查询个数和区间覆盖。可以给每个字母开个线段树解决。

#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,m,cnt[29];
char s[N];

struct segtree {
	struct node {int l,r,cnt,tag;} t[N*4];
	void build(int p,int l,int r,char c) {
		int mid=((t[p].l=l)+(t[p].r=r))/2;
		if(l==r) {t[p].cnt=(s[l]==c); return;}
		build(p*2,l,mid,c), build(p*2+1,mid+1,r,c);
		t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
	}
	void pushdown(int p) {
		t[p*2].tag=t[p*2+1].tag=t[p].tag;
		if(t[p].tag==-1) t[p*2].cnt=t[p*2+1].cnt=0;
		else {
			t[p*2].cnt=t[p*2].r-t[p*2].l+1;
			t[p*2+1].cnt=t[p*2+1].r-t[p*2+1].l+1;
		}
		t[p].tag=0;
	}
	void modify(int p,int x,int y,int k) {
		int l=t[p].l, r=t[p].r, mid=(l+r)/2;
		if(l==x&&r==y) {t[p].cnt=(k>0?r-l+1:0),t[p].tag=k; return;}
		if(t[p].tag) pushdown(p);
		if(y<=mid) modify(p*2,x,y,k);
		else if(x>mid) modify(p*2+1,x,y,k);
		else modify(p*2,x,mid,k), modify(p*2+1,mid+1,y,k);
		t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
	}
	int query(int p,int x,int y) {
		int l=t[p].l, r=t[p].r, mid=(l+r)/2;
		if(l==x&&r==y) return t[p].cnt;
		if(t[p].tag) pushdown(p);
		if(y<=mid) return query(p*2,x,y);
		else if(x>mid) return query(p*2+1,x,y);
		else return query(p*2,x,mid)+query(p*2+1,mid+1,y);
	}
}st[29];

void print(int l=1,int r=n) {
	rep(i,l,r) 
		rep(j,0,25)
			if(st[j].query(1,i,i)) {putchar(j+'a');break;}
	puts(""); 
}

int main() {
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	scanf("%d%d%s",&n,&m,s+1);
	rep(i,0,25) st[i].build(1,1,n,i+'a');
	rep(i,1,m) {
		int l=read(), r=read(), odd=0;
		rep(i,0,25) cnt[i]=st[i].query(1,l,r), odd+=cnt[i]%2;
		if(odd>1) continue;
		rep(i,0,25) st[i].modify(1,l,r,-1);
		int pos=0;
		rep(i,0,25) if(cnt[i]>1) {
			int ycnt=cnt[i];
			cnt[i]/=2;
			st[i].modify(1,l+pos,l+pos+cnt[i]-1,1);
			st[i].modify(1,r-pos-cnt[i]+1,r-pos,1);
			pos+=cnt[i];
			cnt[i]=ycnt;
		}
		if(odd) {
			rep(i,0,25) if(cnt[i]%2==1) {
				st[i].modify(1,l+pos,l+pos,1);
			}
		}
	}
	print();
	return 0;
}

CF992E Nastya and King-Shamans

  • 单点修改
  • 询问是否有 \(i=\sum_{j=1}^{i-1}a_j\)

一般这种题都要对询问进行化简。我们发现,满足要求的 \(i\) 最多只有 \(\log\) 种可能,因为每发现一个可能,下一个就要是上一个的两倍及以上。那考虑如何遍历所有可能。从 \(k=1\) 开始,每次找到满足 \(s_x\ge 2s_k\) 的最小的 \(x\) 即可。这个可以线段树上二分实现。

#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=2e5+9;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,q,a[N];
long long s[N];

struct node {int l,r;long long x,tag;} t[N*4];
void build(int p,int l,int r) {
	int mid=((t[p].l=l)+(t[p].r=r))/2;
	if(l==r) {t[p].x=s[l]; return;}
	build(p*2,l,mid), build(p*2+1,mid+1,r);
	t[p].x=max(t[p*2].x,t[p*2+1].x); 
}
void pushdown(int p) {
	t[p*2].tag+=t[p].tag, t[p*2+1].tag+=t[p].tag;
	t[p*2].x+=t[p].tag, t[p*2+1].x+=t[p].tag;
	t[p].tag=0;
}
void add(int p,int x,int y,int k) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(l==x&&r==y) {t[p].x+=k,t[p].tag+=k; return;}
	pushdown(p);
	if(y<=mid) add(p*2,x,y,k);
	else if(x>mid) add(p*2+1,x,y,k);
	else add(p*2,x,mid,k), add(p*2+1,mid+1,y,k);
	t[p].x=max(t[p*2].x,t[p*2+1].x);
}
int query(int p,long long k) {
	if(t[p].l==t[p].r) return t[p].x>=k?p:-1;
	pushdown(p);
	if(t[p*2].x>=k) return query(p*2,k);
	else if(t[p*2+1].x>=k) return query(p*2+1,k);
	else return -1;
}

int main() {
	n=read(), q=read();
	rep(i,1,n) a[i]=read(), s[i]=s[i-1]+a[i];
	build(1,1,n);
	rep(i,1,q) {
		int pos=read(), val=read();
		add(1,pos,n,val-a[pos]);
		a[pos]=val;
		int k=1;long long sk=a[1];
		while(k) {
			int p=query(1,2*sk);
			if(p==-1) {puts("-1");break;}
			sk=t[p].x, k=t[p].l;
			if(2*a[k]==sk) {printf("%d\n",k);break;}
		}
	}
	return 0;
}

P2572 [SCOI2010]序列操作

一年多前看神仙题,一年多后看傻逼题。

维护区间0/1的个数,最长连续0/1的个数,左起最长连续0/1的个数,右起最长连续0/1的个数,然后tag打是覆盖0/1/不覆盖,以及区间是否要翻转。注意两个tag的问题。显然处理覆盖更加方便,所以我们另其tag的优先顺序为先赋值然后取反。操作比较复杂,所以会共同用的函数分开写成单独的函数调用方便。

#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,m,a[N];

struct node {
	int l,r,len,c[2],ls[2],rs[2],s[2],tg1,tg2;
	void cov(int x) {
		c[x]=ls[x]=rs[x]=s[x]=len;
		c[x^1]=ls[x^1]=rs[x^1]=s[x^1]=0;
		tg1=x, tg2=0;
	}
	void flip() {
		swap(c[0],c[1]), swap(s[0],s[1]),
		swap(ls[0],ls[1]), swap(rs[0],rs[1]);
		tg2^=1;
	}
} t[N*4];
node pushup(node x,node y,node r) {
	r.len=x.len+y.len;
	rep(i,0,1) {
		r.c[i]=x.c[i]+y.c[i];
		r.ls[i]=(x.c[i^1]?x.ls[i]:x.len+y.ls[i]);
		r.rs[i]=(y.c[i^1]?y.rs[i]:y.len+x.rs[i]);
		r.s[i]=max(max(x.s[i],y.s[i]),x.rs[i]+y.ls[i]);
	}
	return r;
}
void pushdown(int p) {
	if(t[p].tg1!=-1) t[p*2].cov(t[p].tg1),t[p*2+1].cov(t[p].tg1);
	if(t[p].tg2) t[p*2].flip(), t[p*2+1].flip();
	t[p].tg1=-1, t[p].tg2=0;
}
void build(int p,int l,int r) {
	t[p].l=l, t[p].r=r, t[p].tg1=-1, t[p].tg2=0, t[p].len=t[p].r-t[p].l+1;
	if(l==r) {t[p].cov(a[l]); return;}
	build(p*2,l,(l+r)/2), build(p*2+1,(l+r)/2+1,r);
	t[p]=pushup(t[p*2],t[p*2+1],t[p]);
}
void cov(int p,int x,int y,int k) { 
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(x==l&&y==r) {t[p].cov(k); return;}
	pushdown(p);
	if(y<=mid) cov(p*2,x,y,k);
	else if(x>mid) cov(p*2+1,x,y,k);
	else cov(p*2,x,mid,k), cov(p*2+1,mid+1,y,k);
	t[p]=pushup(t[p*2],t[p*2+1],t[p]);
}
void flip(int p,int x,int y) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(x==l&&y==r) {t[p].flip(); return;}
	pushdown(p);
	if(y<=mid) flip(p*2,x,y);
	else if(x>mid) flip(p*2+1,x,y);
	else flip(p*2,x,mid), flip(p*2+1,mid+1,y);
	t[p]=pushup(t[p*2],t[p*2+1],t[p]);
}
node query(int p,int x,int y) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(x==l&&y==r) return t[p];
	pushdown(p);
	node tmp;
	if(y<=mid) return query(p*2,x,y);
	else if(x>mid) return query(p*2+1,x,y);
	else return pushup(query(p*2,x,mid),query(p*2+1,mid+1,y),tmp);
}

int main() {
	n=read(), m=read();
	rep(i,1,n) a[i]=read();
	build(1,1,n);
	rep(i,1,m) {
		int opt=read(), l=read()+1, r=read()+1;
		if(opt==0) cov(1,l,r,0);
		else if(opt==1) cov(1,l,r,1);
		else if(opt==2) flip(1,l,r);
		else if(opt==3) printf("%d\n",query(1,l,r).c[1]);
		else printf("%d\n",query(1,l,r).s[1]);
	}
	return 0;
}

后记:tmd一堆细节错误调死人,再也不叫他傻逼题了

CF1114F Please, another Queries on Array?

需要发现一个重要的东西:质数个数其实很少(62)个,所以直接状压就行了。

知道区间质数有哪些就可以轻而易举地算出答案了

\[\varphi(x)=x\prod_{p\in prime, p|x} \frac{p-1}{p} \]

复杂度 \(O(n\log^2 n+62n)\)

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=4e5+9,mod=1e9+7;
typedef pair<int,int> pii;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int n,q,a[N],pr[N],inv[N],cnt;
bool vst[N];

int ksm(int a,int b) {
	if(b==0) return 1;
	else return ksm(a*a%mod,b/2)*(b%2==1?a:1)%mod;
}

void sieve() {
	rep(i,2,300) {
		if(vst[i]) continue;
		pr[++cnt]=i, inv[cnt]=ksm(i,mod-2);
		for(int j=2;i*j<=300;j++) vst[i*j]=1;
	}
}

struct node {int l,r,s,tg1,tg2,prod;} t[N*4];
void build(int p,int l,int r) {
	int mid=((t[p].l=l)+(t[p].r=r))/2;
	if(l==r) {
		rep(i,1,cnt) if(a[l]%pr[i]==0) t[p].s+=(1ll<<i);
		t[p].prod=a[l];
		t[p].tg1=0, t[p].tg2=1;
		return;
	}
	build(p*2,l,mid), build(p*2+1,mid+1,r);
	t[p].s=t[p*2].s|t[p*2+1].s, t[p].prod=t[p*2].prod*t[p*2+1].prod%mod;
	t[p].tg1=0, t[p].tg2=1;
}
void pushdown(int p) {
	t[p*2].s|=t[p].tg1, t[p*2+1].s|=t[p].tg1;
	t[p*2].tg1|=t[p].tg1, t[p*2+1].tg1|=t[p].tg1;
	int len=t[p*2].r-t[p*2].l+1;
	t[p*2].prod=(t[p*2].prod*ksm(t[p].tg2,len))%mod;
	t[p*2].tg2=(t[p*2].tg2*t[p].tg2)%mod;
	len=t[p*2+1].r-t[p*2+1].l+1;
	t[p*2+1].prod=(t[p*2+1].prod*ksm(t[p].tg2,len))%mod;
	t[p*2+1].tg2=(t[p*2+1].tg2*t[p].tg2)%mod;
	t[p].tg1=0, t[p].tg2=1;
}
void modify(int p,int x,int y,int g,int k) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(l==x&&r==y) {
		t[p].s|=k, t[p].tg1|=k;
		t[p].prod=t[p].prod*ksm(g,r-l+1)%mod,
		t[p].tg2=t[p].tg2*g%mod;
		return;
	}
	pushdown(p);
	if(y<=mid) modify(p*2,x,y,g,k);
	else if(x>mid) modify(p*2+1,x,y,g,k);
	else modify(p*2,x,mid,g,k), modify(p*2+1,mid+1,y,g,k);
	t[p].s=t[p*2].s|t[p*2+1].s, t[p].prod=t[p*2].prod*t[p*2+1].prod%mod;
}
pii query(int p,int x,int y) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(l==x&&r==y) return make_pair(t[p].s,t[p].prod);
	pushdown(p);
	if(y<=mid) return query(p*2,x,y);
	else if(x>mid) return query(p*2+1,x,y);
	else {
		pii a=query(p*2,x,mid), b=query(p*2+1,mid+1,y);
		return make_pair(a.first|b.first,a.second*b.second%mod);
	}
}

signed main() {
	n=read(), q=read();
	sieve();
	rep(i,1,n) a[i]=read();
	build(1,1,n);
	rep(i,1,q) {
		char opt[10]; scanf("%s",opt);
		if(opt[0]=='M') {
			int l=read(), r=read(), x=read(), s=0;
			rep(i,1,cnt) if(x%pr[i]==0) s+=(1ll<<i);
			modify(1,l,r,x,s);
		} else {
			int l=read(), r=read();
			pii res=query(1,l,r);
			int ans=res.second, s=res.first;
			rep(i,1,cnt) if(s&(1ll<<i)) ans=(ans*inv[i]%mod*(pr[i]-1)%mod);
			printf("%lld\n",ans);
		}
	} 
	return 0;
}

P5568 [SDOI2008]校门外的区间

把操作全部转化为区间赋值/反转,于是就和序列操作那题一样了。

[数据隐藏]

一些有趣的线段树题需要维护两个tag互相不独立的tag。比如这题 (ZR十连测3A(博客仅自己可见))

P4314 CPU监控

First thing first,假如一个区间出现过整体赋值,再有整体加法,那么相当于第二次整体赋值;即若出现过整体赋值,那么后面的整体加法全部变为整体赋值。
第二点,把父亲的tag下放,相当于把父亲tag的操作序列接到儿子tag的操作序列后面。

这道题的tag需要维护加法tag,赋值tag,历史最大累加tag,以及历史最大赋值tag。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9, inf=0x3f3f3f3f3f3f3f3f;

inline int read() {
    register int x=0, f=1; register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
    return x*f;
}

int T,a[N],E;
char opt[5];

struct node {
	int l,r,m,h,t1,t2,h1,h2; //1加法 2赋值 
	void cov(int k,int hk) {
		m=k, h=max(h,hk);
		t2=k, h2=max(h2,hk);
	}
	void add(int k,int hk) {
		if(t2!=inf) return cov(t2+k,t2+hk);
		h1=max(h1,hk+t1);
		t1+=k, m+=k;
		h=max(h,m-t1+h1);
	}
} t[N*4];
node unite(node x,node y,node r) {
	r.m=max(x.m,y.m), r.h=max(x.h,y.h);
	return r;
}
void build(int p,int l,int r) {
	t[p].t2=inf;
	int mid=((t[p].l=l)+(t[p].r=r))/2;
	if(l==r) {t[p].m=t[p].h=a[l]; return;}
	build(p*2,l,mid), build(p*2+1,mid+1,r);
	t[p]=unite(t[p*2],t[p*2+1],t[p]);
}
void pushdown(int p) {
	t[p*2].add(t[p].t1,t[p].h1), t[p*2+1].add(t[p].t1,t[p].h1);
	t[p].t1=t[p].h1=0;
	if(t[p].t2!=inf) {
		t[p*2].cov(t[p].t2,t[p].h2), t[p*2+1].cov(t[p].t2,t[p].h2);
		t[p].t2=inf;
	}
}
void cov(int p,int x,int y,int k) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(x==l&&y==r) {t[p].cov(k,max(0ll,k)); return;}
	pushdown(p);
	if(y<=mid) cov(p*2,x,y,k);
	else if(x>mid) cov(p*2+1,x,y,k);
	else cov(p*2,x,mid,k), cov(p*2+1,mid+1,y,k);
	t[p]=unite(t[p*2],t[p*2+1],t[p]);
}
void add(int p,int x,int y,int k) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(x==l&&y==r) {t[p].add(k,max(0ll,k)); return;}
	pushdown(p);
	if(y<=mid) add(p*2,x,y,k);
	else if(x>mid) add(p*2+1,x,y,k);
	else add(p*2,x,mid,k), add(p*2+1,mid+1,y,k);
	t[p]=unite(t[p*2],t[p*2+1],t[p]);
}
node query(int p,int x,int y) {
	int l=t[p].l, r=t[p].r, mid=(l+r)/2;
	if(x==l&&y==r) return t[p];
	pushdown(p);
	if(y<=mid) return query(p*2,x,y);
	else if(x>mid) return query(p*2+1,x,y);
	else return unite(query(p*2,x,mid),query(p*2+1,mid+1,y),t[0]);
}

signed main() {
	T=read();
	rep(i,1,T) a[i]=read();
	build(1,1,T);
	E=read();
	rep(i,1,E) {
		scanf("%s",opt);
		int x=read(), y=read();
		if(opt[0]=='Q') printf("%lld\n",query(1,x,y).m);
		else if(opt[0]=='A') printf("%lld\n",query(1,x,y).h);
		else if(opt[0]=='P') add(1,x,y,read());
		else cov(1,x,y,read());
	}
	return 0;
}
posted @ 2021-01-16 12:42  LarsWerner  阅读(12)  评论(0编辑  收藏  举报