我的线段树是坨屎(线段树专题)

不了解线段树的可以从这个入门,写的超棒

 

线段树基础模板:查询区间和和区间最值

不要care我下面时而数组线段树时而结构体线段树。

 

A.hdu 1166 敌兵布阵

:线段树板子题,单点修改区间查询。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=5e4+5;
int Sum[maxn<<2],Add[maxn<<2];
int A[maxn],n;
void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}
void Build(int l,int r,int rt){
	if(l==r) {
		Sum[rt]=A[l];
		return;
	}
	int m=(l+r)>>1;
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	PushUp(rt);
}
void PushDown(int rt,int ln,int rn){
	if(Add[rt]){
		Add[rt<<1]+=Add[rt];
		Add[rt<<1|1]+=Add[rt];
		Sum[rt<<1]+=Add[rt]*ln;
		Sum[rt<<1|1]+=Add[rt]*rn;
		Add[rt]=0;
	}
}
void Update(int L,int C,int l,int r,int rt){
	if(l==r){ 
		Sum[rt]+=C;
		return;
	}
	int m=(l+r)>>1;
	if(L <= m) Update(L,C,l,m,rt<<1);
	else       Update(L,C,m+1,r,rt<<1|1);
	PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt){
	if(L <= l && r <= R){
		return Sum[rt];
	}
	int m=(l+r)>>1;
	PushDown(rt,m-l+1,r-m); 
	
	int ANS=0;
	if(L <= m) ANS+=Query(L,R,l,m,rt<<1);
	if(R >  m) ANS+=Query(L,R,m+1,r,rt<<1|1);
	return ANS;
}
int T; 
int main(){
	std::ios::sync_with_stdio(0);
	cin>>T;
	for(int i=1;i<=T;++i){
		ms(Sum,0),ms(Add,0);
		cin>>n;
		rep(i,1,n)	cin>>A[i];
		Build(1,n,1);
		cout<<"Case "<<i<<":"<<endl;
		string s;
		int x,y;
		while(cin>>s){
			if(s=="End")	break;
			cin>>x>>y;
			if(s=="Query")	cout<<Query(x,y,1,n,1)<<endl;
			else if(s=="Add") Update(x,y,1,n,1);
			else if(s=="Sub") Update(x,-y,1,n,1);
		} 
	}
	return 0;
}

B.hdu 1754 I Hate It

:板子单点更新,区间最值问题。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=2e5+5;
ll Sum[maxn<<2],Add[maxn<<2];
ll A[maxn];
void PushUp(int rt){
	Sum[rt]=max(Sum[rt<<1],Sum[rt<<1|1]);
}
void Build(int l,int r,int rt){
	if(l==r) {
		Sum[rt]=A[l];
		return;
	}
	int m=(l+r)>>1;
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	PushUp(rt);
}

void Update(int L,int C,int l,int r,int rt){
	if(l==r){ 
		Sum[rt]=C;
		return;
	}
	int m=(l+r)>>1;
	if(L <= m) Update(L,C,l,m,rt<<1);
	else       Update(L,C,m+1,r,rt<<1|1);
	PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt){
	if(L <= l && r <= R){
		return Sum[rt];
	}
	int m=(l+r)>>1;
	
	int ANS=-inf;
	if(L <= m) ANS=max(ANS,Query(L,R,l,m,rt<<1));
	if(R >  m) ANS=max(ANS,Query(L,R,m+1,r,rt<<1|1));
	return ANS;
} 
ll n,m;
int main(){
	std::ios::sync_with_stdio(0);
	while(cin>>n>>m){
		ms(Add,0),ms(Sum,0);
		rep(i,1,n)	cin>>A[i];
		Build(1,n,1);
		char x;
		int y,z;
		rep(i,1,m){
			cin>>x>>y>>z;
			if(x=='Q')	cout<<Query(y,z,1,n,1)<<endl;
			else if(x=='U') Update(y,z,1,n,1);
		}
	}

	return 0;
}

C.hdu 1394 Minimum Inversion Number

题意:本题就是求循环移位后逆序数的最小值

思路:先用线段树求出原来序列的逆序对个数,线段树维护1~n的数出现的个数,将给的序列从后往前放,然后每次查询1~a[i]这个区间已经出现的数字个数,出现了几个那就有几个逆序对,说明本应出现在a[i]前面的数字,出现在了a[i]之后。然后每次加上,在更新这个位置上的数字出现次数。然后再从头到尾将那个元素放到最后,这样的改变量是-(a[i]-1) 取消因为a[i]放在最前的产生的逆序对,+(n-a[i])因为a[i]放在最后新产生的逆序对。


#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
using namespace std;
const int maxn=5005;
int n,b[maxn<<2],a[maxn];
void build(int l,int r,int rt) {
	b[rt]=0;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
}
void add(int l,int r,int rt,int v) {
	b[rt]+=1;
	if(l==r) 	return ;
	int mid=(l+r)>>1;
	if(v<=mid)    add(l,mid,rt<<1,v);
	else    add(mid+1,r,rt<<1|1,v);
}
int sum(int l,int r,int rt,int ll,int rr) {
	if(ll<=l && r<=rr) return b[rt];
	int mid=(l+r)>>1;
	int ans=0;
	if(ll<=mid)     ans+=sum(l,mid,rt<<1,ll,rr);
	if(rr>mid)    ans+=sum(mid+1,r,rt<<1|1,ll,rr);
	return ans;
}
int main() {
	int v;
	while(cin>>n) {
		build(1,n,1);
		int ans=0;
		for(int i=1; i<=n; ++i) {
			cin>>a[i];
			++a[i];
		}
		for(int i=1; i<=n; i++) {
			ans+=sum(1,n,1,a[i],n);
			add(1,n,1,a[i]);
		}
		int mmin=ans;
		for(int i=1; i<=n; ++i) {
			ans-=a[i]-1;
			ans+=n-a[i];
			mmin=min(ans,mmin);
		}
		cout<<mmin<<endl;
	}
	return 0;
}

D.hdu 2795 Billboard

题意:有个公告板,大小为h*w,要贴n张公告,每个公告的长度是k,高度固定为1,公告放的要尽可能靠上并尽可能靠左,每给出一张公告,要求这个公告在满足要求的情况下放在了第几层。

思路:线段树维护h行公告板的最长连续区间,查询的时候尽量往左就行了。h很大但是最大只要满足n张海报就行了。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1e6+5;
int n,h,w;
ll Sum[maxn<<2],A[maxn];
void PushUp(int rt) {
	Sum[rt]=max(Sum[rt<<1],Sum[rt<<1|1]);
}
void Build(int l,int r,int rt) {
	if(l==r) {
		Sum[rt]=w;
		return;
	}
	int m=(l+r)>>1;
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	PushUp(rt);
}
void Update(int L,int C,int l,int r,int rt) {
	if(l==r) {
		Sum[rt]+=C;
		return;
	}
	int m=(l+r)>>1;
	if(L <= m) Update(L,C,l,m,rt<<1);
	else       Update(L,C,m+1,r,rt<<1|1);
	PushUp(rt);
}
ll Query(int L,int R,int num,int l,int r,int rt) {
//	cout<<l<<" "<<r<<" "<<num<<endl;
	if(L<=l && r<=R && r-l==0) {
		if(Sum[rt]>=num)	return l;
		else return inf;
	}
	ll m=(l+r)>>1,ans=inf;
//	cout<<"ls "<<Sum[rt<<1]<<" rs "<<Sum[rt<<1|1]<<endl;
	if(L <= m && Sum[rt<<1]>=num)	ans=min(ans,Query(L,R,num,l,m,rt<<1));
	else if(R > m && Sum[rt<<1|1]>=num)	ans=min(ans,Query(L,R,num,m+1,r,rt<<1|1));
	return ans;
}

int main() {
	std::ios::sync_with_stdio(0);
	while(cin>>h>>w>>n) {
		ms(Sum,0);
		if(h>n)	h=n;
		Build(1,h,1);
		int num,cnt;
		rep(i,1,n) {
			cin>>num;
			if(Sum[1]<num)	cout<<-1<<endl;
			else{
				cnt=Query(1,h,num,1,h,1);
				if(cnt==inf)	cout<<-1<<endl;
				else{
					cout<<cnt<<endl;
					Update(cnt,-num,1,h,1);
				}
				
			} 
		}

	}
	return 0;
}

E.hdu 1698 Just a Hook

题意:连续n个棍棒,现进行m次操作,把a~b的棍棒换成c材质的,求总价值,(铜(1初始),银(2),金(3))

思路:线段树的区间更新就行了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
//#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1e5+5;
ll sum[maxn<<2],cg[maxn<<2],n;
il void pushup(int rt){
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
il void pushdown(int rt,int ln,int rn){
	if(cg[rt]){
		cg[rt<<1]=cg[rt];
		cg[rt<<1|1]=cg[rt];
		sum[rt<<1]=ln*cg[rt];
		sum[rt<<1|1]=rn*cg[rt];
		cg[rt]=0;
	}
}
void build(int l,int r,int rt){
	if(l==r){
		sum[rt]=1;
		return ;
	}
	int m=(l+r)>>1;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	pushup(rt);
}
void update(int L,int R,int C,int l,int r,int rt){
	if(L<=l && r<=R){
		sum[rt]=(ll)C*(r-l+1);
		cg[rt]=C;
		return ;
	}
	int m=(l+r)>>1;
	pushdown(rt,m-l+1,r-m);
	if(L <= m) update(L,R,C,l,m,rt<<1);
	if(R >  m) update(L,R,C,m+1,r,rt<<1|1); 
	pushup(rt);
}
int T,q,x,y,z;
int main(){
	std::ios::sync_with_stdio(0);
	cin>>T;
	rep(i,1,T){
		ms(cg,0);
		cin>>n;
		build(1,n,1);
		cin>>q;
		while(q--){
			cin>>x>>y>>z;
			update(x,y,z,1,n,1);
			
		}
		cout<<"Case "<<i<<": The total value of the hook is "<<sum[1]<<"."<<endl;
	}
	return 0;
}

F.poj 3468 A Simple Problem with Integers

:板子题区间更新。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1e5+5;
ll Sum[maxn<<2],Add[maxn<<2];
ll A[maxn];
void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}
void Build(int l,int r,int rt){
	if(l==r) {
		Sum[rt]=A[l];
		return;
	}
	int m=(l+r)>>1;
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	PushUp(rt);
}
void PushDown(int rt,int ln,int rn){
	if(Add[rt]){
		Add[rt<<1]+=Add[rt];
		Add[rt<<1|1]+=Add[rt];
		Sum[rt<<1]+=Add[rt]*ln;
		Sum[rt<<1|1]+=Add[rt]*rn;
		Add[rt]=0;
	}
}
void Update(int L,int C,int l,int r,int rt){
	if(l==r){ 
		Sum[rt]+=C;
		return;
	}
	int m=(l+r)>>1;
	if(L <= m) Update(L,C,l,m,rt<<1);
	else       Update(L,C,m+1,r,rt<<1|1);
	PushUp(rt);
}
void Update(int L,int R,int C,int l,int r,int rt){
	if(L <= l && r <= R){
		Sum[rt]+=C*(r-l+1);
		Add[rt]+=C;
		return ; 
	}
	int m=(l+r)>>1;
	PushDown(rt,m-l+1,r-m);
	if(L <= m) Update(L,R,C,l,m,rt<<1);
	if(R >  m) Update(L,R,C,m+1,r,rt<<1|1); 
	PushUp(rt);
}
ll Query(int L,int R,int l,int r,int rt){
	if(L <= l && r <= R){
		return Sum[rt];
	}
	int m=(l+r)>>1;
	PushDown(rt,m-l+1,r-m); 
	
	ll ANS=0;
	if(L <= m) ANS+=Query(L,R,l,m,rt<<1);
	if(R >  m) ANS+=Query(L,R,m+1,r,rt<<1|1);
	return ANS;
}
int n,q;
int main(){
	std::ios::sync_with_stdio(0);
	while(cin>>n>>q){
		ms(Add,0),ms(Sum,0);
		rep(i,1,n)	cin>>A[i];
		Build(1,n,1);
		char tp;
		int x,y,z;
		rep(i,1,q){
			cin>>tp;
			if(tp=='Q'){
				cin>>x>>y;
				cout<<Query(x,y,1,n,1)<<endl;
			}
			else if(tp=='C'){
				cin>>x>>y>>z;
				Update(x,y,z,1,n,1);
			}
		}
	}
	
	return 0;
}

G.poj 2528 Mayor's posters

题意:有一块足够长的墙了给竞选人贴海报,后贴的可能会把衣面贴的给覆盖掉,问最有多少不同的海报是能看到的。

思路:首先海报l,r的范围很大,但是只有10000张海报,所以我们先需要离散化l和r,线段树维护的就是区间是否被全部覆盖。

贴海报的顺序我们从后往前贴,每次query一下该海报的l,r区间是否被全部覆盖,就可知最后能不能被看见了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
//#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1e5+5;
bool vis[maxn<<2],cg[maxn<<2],fg=0;
il void pushup(int rt){
	vis[rt]=vis[rt<<1] && vis[rt<<1|1]; 
}
il void pushdown(int rt,int ln,int rn){
	if(cg[rt]){
		cg[rt<<1]=cg[rt];
		cg[rt<<1|1]=cg[rt];
		vis[rt<<1]=1;
		vis[rt<<1|1]=1;
		cg[rt]=0;
	}
}
void update(int L,int R,int l,int r,int rt){
	if(L<=l && r<=R){
		vis[rt]=1;
		cg[rt]=1;
		return ;
	}
	int m=(l+r)>>1;
	pushdown(rt,m-l+1,r-m);
	if(L <= m) update(L,R,l,m,rt<<1);
	if(R >  m) update(L,R,m+1,r,rt<<1|1); 
	pushup(rt);
}
void query(int L,int R,int l,int r,int rt){
	if(vis[rt])	return ;
	if(L<=l && r<=R && !vis[rt]){
		fg=1;
		return;
	}
	int m=(l+r)>>1;
	pushdown(rt,m-l+1,r-m);
	if(L <= m) query(L,R,l,m,rt<<1);
	if(fg)	return ;
	if(R >  m) query(L,R,m+1,r,rt<<1|1);
}
int T,n,x,y;
struct node{
	int id,x;
}pp[maxn];
bool cmp(node a,node b){
	return a.x<b.x;
}
bool recmp(node a,node b){
	if(a.id==b.id)	return a.x<b.x;
	else return a.id>b.id;
}
int main(){
	std::ios::sync_with_stdio(0);
	sc(T);
	while(T--){
		sc(n);
		ms(vis,0),ms(cg,0);
		for(int i=0;i<2*n;i+=2){
			SC(pp[i].x,pp[i+1].x);
			pp[i].id=pp[i+1].id=i;
		}
		sort(pp,pp+2*n,cmp);
		int pre=0,cnt=0;
		for(int i=0;i<2*n;++i){
			if(pp[i].x==pre)	pp[i].x=cnt;
			else{
				pre=pp[i].x;
				pp[i].x=++cnt;
			}
		}
		sort(pp,pp+2*n,recmp);
		int ans=0,le,ri;
		for(int i=0;i<2*n;i+=2){
			fg=0;
			le=pp[i].x,ri=pp[i+1].x;
			query(le,ri,1,2*n,1);
			if(fg)	ans++;
			update(le,ri,1,2*n,1);
		}
		cout<<ans<<endl; 
	}
	return 0;
}

H.poj 3225 Help with Intervals

题意:就是根据题目给你的运算法则,完成集合的运算最后输出集合的运算。

思路:这个题感觉有很多难点,闭合区间可以+1,-1来完成,但是我们发现我们无法表示(1,2),这里的解决方案就是将l和r都乘2,喵呀。区间操作大家都是一样的,如下。

对于区间的交并补问题,可以转化为区间覆盖问题,若T区间为[a,b]。

U T:[a,b]覆盖为1.

I T:[0,a-1] [b+1,maxn] 覆盖为0

D T:[a,b]覆盖为0

C T:[0,a-1] [b+1,maxn] 覆盖为0,[a,b]取反

S T:[a,b]取反

还有就是异或操作了,对于一个区间如果值全都一样,那我们就直接异或了,反之我们就不能用一个值去代表这个区间的值,即不能直接异或。还有就是两次异或就相当于没异或。

所以我们在线段树的结构体里定义了l,r,cov,rev;

//代表区间 ,是否被覆盖 ,异或标记 

//cov 0:区间全是0,1:区间全是1,-1:区间都有 

//rev 0: 不需要异或 ,1:需要异或 

对于我们不能直接异或的区间,我们就去异或他的异或标记,pushdown的话就是往下传递了,如果当前父亲节点区间被覆盖,那孩子节点也是被覆盖,并且抹除异或标记,将该父亲cov先标记为-1,因为下面的操作可能会改变父亲节点的覆盖状态。query那就把该是1的地方都标记到数组上就行了,最后输出不要忘了一开始的*2操作了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
//#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1e6+5;
const int mx=150000;
bool la[maxn];
struct node{
	int l,r,cov,rev;//代表区间 ,是否被覆盖 ,异或标记 
	//cov 0:区间全是0,1:区间全是1,-1:区间都有 
	//rev 0: 不需要异或 ,1:需要异或 
}tree[maxn<<2];
void build(int l,int r,int rt){
	tree[rt].l=l,tree[rt].r=r;
	tree[rt].cov=tree[rt].rev=0;
	if(l==r)	return ;
	int m=(l+r)>>1;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
}
void pushdown(int rt){
	if(tree[rt].l==tree[rt].r)	return ;
	if(tree[rt].cov!=-1){
		tree[rt<<1].cov=tree[rt].cov;
		tree[rt<<1|1].cov=tree[rt].cov;
		tree[rt].cov=-1;
		tree[rt].rev=0;
		tree[rt<<1].rev=0;
		tree[rt<<1|1].rev=0;
	}
	else if(tree[rt].rev){
		if(tree[rt<<1].cov!=-1) tree[rt<<1].cov^=1;
		else	tree[rt<<1].rev^=1;
		if(tree[rt<<1|1].cov!=-1)  tree[rt<<1|1].cov^=1;
		else	tree[rt<<1|1].rev^=1;
		tree[rt].rev=0;
	}
} 
void update(int l,int r,int rt,int id){
//	cout<<l<<" "<<r<<" "<<rt<<" "<<id<<endl;
	if(l>r)	return ;
	if(tree[rt].l>=l && tree[rt].r<=r){
		if(id==0 || id==1){
			tree[rt].cov=id;
			tree[rt].rev=0;
		}
		else if(id==2){
			if(tree[rt].cov!=-1)	tree[rt].cov^=1;
			else	tree[rt].rev^=1;
		}
		return ;
	}
	pushdown(rt);
	int m=(tree[rt].l+tree[rt].r)>>1;
	if(r<=m) update(l,r,rt<<1,id);
	else if(l>m) update(l,r,rt<<1|1,id);
	else update(l,m,rt<<1,id),update(m+1,r,rt<<1|1,id);

}
void query(int rt){
	if(tree[rt].cov==1){
		for(int i=tree[rt].l;i<=tree[rt].r;++i)	la[i]=1;
		return ; 
	}
	if(tree[rt].cov==0 || tree[rt].l==tree[rt].r)	return ;
	pushdown(rt);
	query(rt<<1),query(rt<<1|1);
}
char t,x,y;
int a,b;
int main() {
	build(0,mx,1); 
	while(scanf("%c %c%d,%d%c",&t,&x,&a,&b,&y)!=EOF){
		int le=a*2,ri=b*2;
		if(x=='(')	le++;
		if(y==')')	ri--;
		
		if(t=='U')	update(le,ri,1,1);
		if(t=='I'){
			update(0,le-1,1,0);
			update(ri+1,mx,1,0);
		}
		if(t=='D')	update(le,ri,1,0);
		if(t=='C'){
			update(0,le-1,1,0);
			update(ri+1,maxn,1,0);
			update(le,ri,1,2);
		}	
		if(t=='S')	update(le,ri,1,2);
		getchar();
	}
	query(1);
	
	int l=0,flag=0;
	for(int i=0;i<mx;++i){
		if(la[i] == 1 && (i == 0 || la[i-1] == 0)) l = i;
		if(la[i] == 1 && (i == mx-1 || la[i+1] == 0)) {
			if(flag == 0) flag = 1;
			else cout<<" ";
			
			if(l%2)	cout<<"(";
			else cout<<"[";
			cout<<l/2<<","<<(i+1)/2;
			if(i%2)	cout<<")";
			else cout<<"]";
		}
	}
	if(flag==0) cout<<"empty set"<<endl;
	else cout<<endl;
	return 0;
}

I.poj 3667 Hotel

题意:n个连续的房间m个操作。操作分两种,第一种以1 x形式给出,找到最左的能连续容下x个人的连续房间,并输出左端点的编号,如果找不到就输出0.第二种以2 l x的形式给出,表示以l为起点的x个房间都清空。

思路:用线段树来维护区间最大连续即可,一个区间的连续可能是从左开始的连续,也可能是从右开始,也可能是中间的。所以线段树需要同时维护区间的最大左连续,最大右连续,最大连续。这样父节点的最大连续才能从三种可能的最大连续得出。查询的时候能左就左就好了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
//#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=5e4+5;
int sum[maxn<<2],lz[maxn<<2],lc[maxn<<2],rc[maxn<<2];
il void pushup(int rt,int l,int r){
	int m=(l+r)>>1;
	if(sum[rt<<1]==(m-l+1) && sum[rt<<1|1]==(r-m)){
		sum[rt]=lc[rt]=rc[rt]=r-l+1;
		return ;
	}	
	sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
	lc[rt]=lc[rt<<1],rc[rt]=rc[rt<<1|1];
	if(sum[rt<<1]==(m-l+1)) lc[rt]+=lc[rt<<1|1];//	左连续 右连续
	if(sum[rt<<1|1]==(r-m))	rc[rt]+=rc[rt<<1];
	sum[rt]=max(max(sum[rt],rc[rt<<1]+lc[rt<<1|1]),max(lc[rt],rc[rt]));
	//中间连续 ,左连续 ,右连续 
}
il void pushdown(int rt,int l,int r){
	if(lz[rt]==1){ //1为占据操作 ,2为清空操作 
		sum[rt<<1]=lc[rt<<1]=rc[rt<<1]=0;
		sum[rt<<1|1]=lc[rt<<1|1]=rc[rt<<1|1]=0;
		lz[rt<<1]=1;
		lz[rt<<1|1]=1;
		lz[rt]=0;
	}
	else if(lz[rt]==2){
		int m=(l+r)>>1;
		sum[rt<<1]=lc[rt<<1]=rc[rt<<1]=m-l+1;
		sum[rt<<1|1]=lc[rt<<1|1]=rc[rt<<1|1]=r-m;
		lz[rt<<1]=2;
		lz[rt<<1|1]=2;
		lz[rt]=0;
	}
}
void build(int l,int r,int rt){
	if(l==r) {
		sum[rt]=lc[rt]=rc[rt]=1;
		return;
	}
	int m=(l+r)>>1;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	pushup(rt,l,r);
} 
void update(int L,int R,int l,int r,int rt,bool id){
	if(L <= l && r <= R){
		if(id==0){ //占据 
			sum[rt]=lc[rt]=rc[rt]=0;
			lz[rt]=1;
		} 
		else if(id==1){ //清空 
			sum[rt]=lc[rt]=rc[rt]=r-l+1;
			lz[rt]=2; 
		}
		return ; 
	}
	int m=(l+r)>>1;
	pushdown(rt,l,r);
	if(L <= m) update(L,R,l,m,rt<<1,id);
	if(R >  m) update(L,R,m+1,r,rt<<1|1,id); 
	pushup(rt,l,r);
}
int query(int l,int r,int num,int rt){
	if(l==r)	return l;
	int m=(l+r)>>1;
	pushdown(rt,l,r);
	
	int lson=rt<<1,rson=rt<<1|1;
	if(sum[lson]>=num)	return query(l,m,num,lson);
	else if(rc[lson]+lc[rson]>=num) return m-rc[lson]+1;
	else if(sum[rson]>=num) return query(m+1,r,num,rson);
}
int n,m;
int main(){
	std::ios::sync_with_stdio(0);
	SC(n,m);
	build(1,n,1);
	int x,y,z;
	rep(i,1,m){
		sc(x);
		if(x==1){
			sc(y);
			if(sum[1]<y)	cout<<0<<endl;
			else{
				int tp=query(1,n,y,1);
				if(tp==inf)	cout<<0<<endl;
				else{
					cout<<tp<<endl;
					update(tp,tp+y-1,1,n,1,0);
				} 
			}
		}
		else if(x==2){
			SC(y,z);
			update(y,y+z-1,1,n,1,1);
		}
	}
	return 0;
}


 

线段树+扫描线

一篇讲的很不错的博客

 

J.hdu 1542 Atlantis

题意:就是求矩阵的面积并。

思路:我在做这题时还没有看到上面那篇博客,看的另一篇戳我,也讲很好,不过就是扫描的方式有点不同,就是先按照出现的y坐标将横向分为一个个单元部分(不可再分),然后由x从小到大,用竖线扫描。结构体中的x就是上一个来到这个区间的x。线段树的每个节点的cnt,如果cnt=0时,表示该节点控制的范围没有被覆盖,只要cnt!=0(cnt不可能小于0,其实就是大于0) 就表示该节点控制的几块区间仍然被覆盖。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=500;
double y[maxn];
struct node{
	double low,up,x;
	int cnt;
	bool fg;
}tree[maxn<<2];
struct tdd{
	double x,y_low,y_up;
	int fg;
}line[maxn];
void build(int l,int r,int rt){
	tree[rt].cnt=0,tree[rt].x=0,tree[rt].fg=0;
	tree[rt].low=y[l],tree[rt].up=y[r];
//	cout<<l<<" "<<r<<" "<<rt<<" lowup "<<tree[rt].low<<" "<<tree[rt].up<<endl;
	if((l+1)==r){
		tree[rt].fg=1; //最小区间 
		return ;
	}
	int m=(l+r)>>1;
	build(l,m,rt<<1);
	build(m,r,rt<<1|1); 
}
double query(double nx,double nlow,double nup,int rt,int nfg){
//	cout<<"query "<<tree[rt].low<<" "<<tree[rt].up<<endl;
	if(nlow>=tree[rt].up || nup<=tree[rt].low)	return 0;
	
	if(tree[rt].fg){
		double rs=0;
		if(tree[rt].cnt>0){
			rs=(nx-tree[rt].x)*(tree[rt].up-tree[rt].low);
			tree[rt].cnt+=nfg;
			tree[rt].x=nx;
		}
		else{
			tree[rt].cnt+=nfg;
			tree[rt].x=nx;
		}
		return rs;
	}
	double ans=0;
	ans+=query(nx,nlow,nup,rt<<1,nfg);
	ans+=query(nx,nlow,nup,rt<<1|1,nfg);
	return ans;
}
int n;
bool cmp(tdd a,tdd b){
	return a.x<b.x;
}
int main(){
	int count=0;
	while(cin>>n && n){
		int cnt=0;
		double x1,y1,x2,y2;
		rep(i,1,n){
			cin>>x1>>y1>>x2>>y2;
			y[++cnt]=y1;
			line[cnt]=tdd{x1,y1,y2,1};
			y[++cnt]=y2;
			line[cnt]=tdd{x2,y1,y2,-1};
		}
		sort(y+1,y+1+cnt);
		sort(line+1,line+1+cnt,cmp);
		build(1,cnt,1);
		double ans=0;
		rep(i,1,cnt){
			ans+=query(line[i].x,line[i].y_low,line[i].y_up,1,line[i].fg);
		}
		cout<<"Test case #"<<(++count)<<endl;
		printf("Total explored area: %.2lf\n\n",ans);
	}
	return 0;
}

K.hdu 1828 Picture

题意:矩形的周长并。

思路:只会蠢得扫两遍的方法,一遍算横向的长,一遍算纵向的长。先将y坐标投影到y轴,然后按照x从小到大,用竖线扫描。

第一条线的结果直接加上,之后答案加上每次tree[1].sum的变化值即可。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(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;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=5e3+5;
int x[maxn<<1],y[maxn<<1];
struct rec {
	int k,l,r,fg;
} a[maxn<<1],b[maxn<<1];
bool cmp(rec a,rec b) {
	return a.k<b.k;
}
struct node {
	int sum,l,r,cnt;
} tree[maxn<<2];
void pushup(int rt,int l,int r) {
	if(l==r)	tree[rt].sum=0;
	else if(tree[rt].cnt>0)	tree[rt].sum=tree[rt].r-tree[rt].l;
	else tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
}
void build(int l,int r,int rt,int *t) {
	tree[rt].cnt=0;
	tree[rt].l=t[l],tree[rt].r=t[r];
	pushup(rt,l,r);
	if(l==r || (l+1)==r) 	return ;
	int m=(l+r)>>1;
	build(l,m,rt<<1,t);
	build(m,r,rt<<1|1,t);
}
void update(int l,int r,int rt,int fg) {
	if(l==tree[rt].l && r==tree[rt].r) { 
		tree[rt].cnt+=fg;
		pushup(rt,l,r);
		return ;
	}
	if(l>=tree[rt<<1|1].l) update(l,r,rt<<1|1,fg);
	else if(r<=tree[rt<<1].r) update(l,r,rt<<1,fg);
	else{
		update(l,tree[rt<<1].r,rt<<1,fg);
		update(tree[rt<<1|1].l,r,rt<<1|1,fg);
	}
	pushup(rt,l,r);
}
int n,x1,x2,y1,y2;
int main() {
	while(sc(n)!=EOF) {
		rep(i,1,n) {
			SC(x1,y1),SC(x2,y2);
			x[i]=x1,x[i+n]=x2;
			y[i]=y1,y[i+n]=y2;
			a[i-1]=rec {x1,y1,y2,1};
			a[i+n-1]=rec {x2,y1,y2,-1};
			b[i-1]=rec {y1,x1,x2,1};
			b[i+n-1]=rec {y2,x1,x2,-1};
		}
		sort(x+1,x+2*n+1);
		sort(y+1,y+2*n+1);
		sort(a,a+2*n,cmp);
		sort(b,b+2*n,cmp);
		int ans=0,pre;
		int leny=unique(y+1,y+2*n+1)-(y+1),lenx=unique(x+1,x+2*n+1)-(x+1);
		build(1,leny,1,y);
		update(a[0].l,a[0].r,1,a[0].fg);
		ans+=tree[1].sum;
		pre=tree[1].sum;
		for(int i=1; i<2*n; ++i) {
			update(a[i].l,a[i].r,1,a[i].fg);
			ans+=abs(tree[1].sum-pre);
			pre=tree[1].sum;
		}

		build(1,lenx,1,x);
		update(b[0].l,b[0].r,1,b[0].fg);
		ans+=tree[1].sum;
		pre=tree[1].sum;
		for(int i=1; i<2*n; ++i) {
			update(b[i].l,b[i].r,1,b[i].fg);
			ans+=abs(tree[1].sum-pre);
			pre=tree[1].sum;
		}
		cout<<ans<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值