1. 程式人生 > 實用技巧 >#線段樹,離散#nssl 1476 聯

#線段樹,離散#nssl 1476 聯


分析

由於下標過大,考慮離散,不僅僅是區間左右端點
假設只有一個區間從1到\(x\),那麼修改後答案應該是\(x+1\)
所以說還要記錄右端點+1的位置,你以為這就能A了嗎
為了避免標記被覆蓋,無論是否找到區間,都要下傳標記,並且如果當前標記為異或,
那麼在修改完之後原來的標記異或抵消,全0變全1,全1變全0,查詢就類似權值線段樹的方法就好了
時間複雜度\(O(nlog_2n)\)


程式碼

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
typedef long long lll;
const int N=300011;
int lazy[N<<2],z[N],m,n;
lll w[N<<2],b[N],l[N],r[N];
inline lll iut(){
	rr lll ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(lll ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline void doit(int k,int l,int r,int z){
	if (!z) return;
	if (z==1) w[k]=r-l+1;
		else if (z==2) w[k]=0;
		    else w[k]=r-l+1-w[k];
	if (z==3) lazy[k]^=z;
	    else lazy[k]=z;
}
inline void pdown(int k,int l,int r){
	if (!lazy[k]||l==r) return; 
	rr int mid=(l+r)>>1;
	doit(k<<1,l,mid,lazy[k]);
	doit(k<<1|1,mid+1,r,lazy[k]);
	lazy[k]=0;
}
inline void update(int k,int l,int r,int x,int y,int z){
	pdown(k,l,r);
	if (l==x&&r==y){doit(k,l,r,z); return;}
	rr int mid=(l+r)>>1;
	if (y<=mid) update(k<<1,l,mid,x,y,z);
	else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
	    else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
	w[k]=w[k<<1]+w[k<<1|1];
}
inline signed query(int k,int l,int r){
	if (l==r) return l;
	rr int mid=(l+r)>>1; pdown(k,l,r);
	if (w[k<<1]==mid-l+1) return query(k<<1|1,mid+1,r);
	    else return query(k<<1,l,mid);
}
signed main(){
	m=iut(),b[n=1]=1;
	for (rr int i=1;i<=m;++i){
		z[i]=iut(),l[i]=iut(),r[i]=iut();
	    b[++n]=l[i],b[++n]=r[i],b[++n]=r[i]+1;
	}
	sort(b+1,b+1+n),n=unique(b+1,b+1+n)-b-1;
	for (rr int i=1;i<=m;++i){
		l[i]=lower_bound(b+1,b+1+n,l[i])-b;
		r[i]=lower_bound(b+1,b+1+n,r[i])-b;
		update(1,1,n,l[i],r[i],z[i]);
		print(b[query(1,1,n)]),putchar(10); 
	}
	return 0;
}