线段树2

先来看一下这道模板题:

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

 

posted @ 2019-09-01 16:12  CZD648  阅读(269)  评论(0编辑  收藏  举报
Live2D