zoukankan      html  css  js  c++  java
  • 【洛谷P2846】光开关【线段树】

    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P2846
    给出一个01串,每次有两种操作:

    • 0 x y0\ x\ y,表示将xxyy之间全部取反。
    • 1 x y1\ x\ y,表示输出xxyy之间1的个数。

    思路:

    首先,这是一道三倍经验题。
    P2574 XOR的艺术
    P3870 [TJOI2009]开关
    (这两道题可以用分块做,但是光开关用分块会T)


    这道题其实就是一个裸的线段树。用tree[x].ltree[x].ltree[x].rtree[x].r表示这个区间的左右端点,tree[x].numtree[x].num表示这个区间有多少个1,tree[x].lazytree[x].lazy就是懒惰标记。
    其中只有tree[x].lazytree[x].lazy和线段树模板不一样。由于很明显如果我们将同一个区间取反两次,那么就是没有取反的意思。所以,tree[x].lazytree[x].lazy其实只要表示这个区间是否被按了奇数次就可以了(按偶数次其实就是按很多个两次,依旧没变),所以tree[x].lazytree[x].lazy的取值就只会是0或1(0表示按了偶数次,1表示按了奇数次),每次更新时异或1即可。
    时间复杂度:O(nlogn)O(nlogn)


    分块的做法也稍微提一下。可以将这个总区间分成n\sqrt{n}个小区间,每次直接在每个小区间内更新,与分块模板也很像。
    时间复杂度:O(nn)O(n\sqrt{n})


    线段树模板:https://blog.csdn.net/SSL_ZYC/article/details/81045174
    分块模板:https://blog.csdn.net/SSL_ZYC/article/details/81978158


    代码:

    #include <cstdio>
    #define N 1000100
    using namespace std;
    
    int n,m,w,x,y;
    
    struct node
    {
    	int l,r,lazy,num;
    }tree[N*3];
    
    void make(int x) 
    {
    	if (tree[x].l==tree[x].r) return;
    	int mid=(tree[x].l+tree[x].r)/2;
    	tree[x*2].l=tree[x].l;
    	tree[x*2].r=mid;
    	tree[x*2+1].l=mid+1;
    	tree[x*2+1].r=tree[x].r;
    	make(x*2);
    	make(x*2+1);
    }
    
    void pushdown(int x)  //下传标记
    {
    	if (tree[x].lazy)
    	{
    		tree[x*2].num=tree[x*2].r-tree[x*2].l+1-tree[x*2].num;
    		tree[x*2].lazy^=1;
    		tree[x*2+1].num=tree[x*2+1].r-tree[x*2+1].l+1-tree[x*2+1].num;
    		tree[x*2+1].lazy^=1;
    		tree[x].lazy=0;
    	}
    }
    
    int ask(int x,int l,int r)  //查找
    {
    	if (tree[x].l==l&&tree[x].r==r) return tree[x].num;
    	if (tree[x].l==tree[x].r) return 0;
    	pushdown(x);
    	int mid=(tree[x].l+tree[x].r)/2;
    	if (l>mid) return ask(x*2+1,l,r);
    	if (r<=mid) return ask(x*2,l,r);
    	return ask(x*2,l,mid)+ask(x*2+1,mid+1,r);
    }
    
    void change(int x,int l,int r)  //修改
    {
    	if (tree[x].l==l&&tree[x].r==r)
    	{
    		tree[x].num=tree[x].r-tree[x].l+1-tree[x].num;  //更新每个区间的值
    		tree[x].lazy^=1;
    		return;
    	}
    	if (tree[x].l==tree[x].r) return;
    	pushdown(x);
    	int mid=(tree[x].l+tree[x].r)/2;
    	if (l>mid)
    	{
    		change(x*2+1,l,r);
    		tree[x].num=tree[x*2].num+tree[x*2+1].num;  //更新每个区间的值
    		return;
    	}
    	if (r<=mid)
    	{
    		change(x*2,l,r);
    		tree[x].num=tree[x*2].num+tree[x*2+1].num;  //更新每个区间的值
    		return;
    	}
    	change(x*2,l,mid);
    	change(x*2+1,mid+1,r);
    	tree[x].num=tree[x*2].num+tree[x*2+1].num;  //更新每个区间的值
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	tree[1].l=1;
    	tree[1].r=n;
    	make(1);
    	while (m--)
    	{
    		scanf("%d%d%d",&w,&x,&y);
    		if (w)
    		{
    			printf("%d\n",ask(1,x,y));
    		}
    		else
    		{
    			change(1,x,y);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Educational Codeforces Round 6
    Educational Codeforces Round 5
    [Educational Round 5][Codeforces 616F. Expensive Strings]
    [Codeforces Round #508 (Div. 2)][Codeforces 1038E. Maximum Matching]
    Codeforces Round #509 (Div. 2)
    从零开始编写一个vue插件
    Web前端错题模糊题记录
    vue中响应式props办法
    vue中router-link的click事件失效的解决办法
    ubuntu修改顶栏颜色
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998615.html
Copyright © 2011-2022 走看看