线段树2

匿名 (未验证) 提交于 2019-12-02 23:57:01

先来看一下这道模板题:

https://www.luogu.org/problem/P2846

题目大意:

开始所有灯都是暗的,

改变:每次操作可以改变两盏灯之间所有灯的状态(亮变暗,暗变亮),

查询:每次查询两盏灯之间亮的灯的个

 

这道题与线段树1中讲的那道题大部分都是一样的,只有一点,

那就是如何在线段树上区间修改

很明显,如果在线段树上执行n次单点修改,时间复杂度会达到O(n^2logn)

比朴素算法更劣,所以要找到更好的解决办法

我们可以在这棵树上的某些节点上标记lazy

表示这个节点对应的区间都要修改

但该节点的所有后代节点的num值都不修改

只修改当前节点的num值

但由于打上了lazy下次还能找到该节点

如果查询时遇到lazy标记的节点

就将标记下沉(取消该节点的lazy,并lazy标记上它的两个子节点,再修改它两个子节点的num值)

这样就能用O(logn)完美解决区间修改问题

 

再具体说一下这道题,这道题需要用上状态压缩

比如将

开、开、关、开、关、关

用二进制数110100来表示

变为十进制就是52,这样就可以用52表示 开、开、关、开、关、关这个状态

这样每个节点num存的就是它表示的区间中的状态对应的十进制数

 

最后,给大家看一下代码:

 

#include<bits/stdc++.h> using namespace std; int num[100000*4]; bool lazy[100000*4]; void build(int l,int r,int root) { 	if(l==r) 	{ 		num[root]=0; 		return; 	} 	int mid=(l+r)/2; 	build(l,mid,root*2); 	build(mid+1,r,root*2+1); 	num[root]=num[root*2]+num[root*2+1]; } void change(int p,int q,int l,int r,int root) { 	//printf("%d %d %d %d %d\n",p,q,l,r,root); 	if(p==l && q==r) 	{ 		lazy[root]=1-lazy[root]; 		num[root]=r-l+1-num[root]; 		//cout<<num[root]<<" "<<root<<endl; 		return; 	} 	int mid=(l+r)/2; 	if(lazy[root]) 	{ 		lazy[root*2]=1-lazy[root*2]; 		lazy[root*2+1]=1-lazy[root*2+1]; 		lazy[root]=0; 		num[root*2]=mid-l+1-num[root*2]; 		num[root*2+1]=r-mid-num[root*2+1]; 	} 	if(q<=mid) change(p,q,l,mid,root*2); 	else if(p>=mid+1) change(p,q,mid+1,r,root*2+1); 	else change(p,mid,l,mid,root*2),change(mid+1,q,mid+1,r,root*2+1); 	num[root]=num[root*2]+num[root*2+1]; } int search(int p,int q,int l,int r,int root) { 	if(p==l && q==r) 	{ 		return num[root]; 	} 	int mid=(l+r)/2; 	if(lazy[root]) 	{ 		lazy[root*2]=1-lazy[root*2]; 		lazy[root*2+1]=1-lazy[root*2+1]; 		lazy[root]=0; 		num[root*2]=mid-l+1-num[root*2]; 		num[root*2+1]=r-mid-num[root*2+1]; 	} 	if(q<=mid) return search(p,q,l,mid,root*2); 	else if(p>=mid+1) return search(p,q,mid+1,r,root*2+1); 	else return search(p,mid,l,mid,root*2)+search(mid+1,q,mid+1,r,root*2+1); } int main() { 	int n,w; 	scanf("%d%d",&n,&w); 	for(int i=1;i<=w;i++) 	{ 		int t; 		int x,y; 		scanf("%d%d%d",&t,&x,&y); 		if(t==0) change(x,y,1,n,1); 		else if(t==1) printf("%d\n",search(x,y,1,n,1)); 	} 	return 0; }

 

最后,再给大家推荐几到简单的养身模板

https://www.luogu.org/problem/P1937

https://www.luogu.org/problem/P1438

https://www.luogu.org/problem/U84988

https://www.luogu.org/problem/P1533

https://www.luogu.org/problem/P2574

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!