• 洛谷 P3870 [TJOI2009]开关 题解


    原题链接

    前置知识:

    线段树的单点、区间的修改与查询。

    一看,我们需要维护两个操作:

    1. 区间取反;

    2. 区间求和。

    (因为区间 (1) 的个数,就是区间的和)

    典型的 线段树

    如果你只会线段树的 区间修改,单点修改,区间查询,单点查询 的话,这题的 “取反” 是个难题。

    但是,这个数组有个性质:

    (a_i in {0,1})

    也就是说,假设一个数组一开始这样子:

    (1) (2) (3) (4)
    (a_i) (0) (1) (0) (0)
    (b_i) (1) (0) (1) (1)

    翻转过后,你会发现:

    翻转后的区间和 = 区间长度 - 区间和。

    因为 原来的区间和(1) 的个数,减掉 (1) 的个数就是 (0) 的个数,而 (0) 翻转后就是 (1),会对答案产生贡献。

    下面区间翻转的标记叠加怎么办?

    显然,翻转 偶数 次直接变成 (0),因为等于没有翻转;翻转 奇数 次变成 (1),因为等于翻转 (1) 次。

    那么,每次翻转在标记上 异或 一下就行。

    (异或之后,(0 gets 1)(1 gets 0)

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=1e5+1;
    #define L (i<<1)
    #define R (i<<1)+1
    
    inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    struct tree{
    	int l,r,sumi;
    	int tag;
    };
    tree t[N<<2];
    int n,m;
    
    inline void update(int i) {
    	t[i].sumi=t[L].sumi+t[R].sumi;
    }
    
    inline void build_tree(int i,int l,int r) {
    	t[i].l=l; t[i].r=r; t[i].tag=0; t[i].sumi=0;
    	if(l==r) return; int mid=(l+r)>>1;
    	build_tree(L,l,mid);
    	build_tree(R,mid+1,r); 
    } //建树
    
    inline void pushdown(int i) {
    	int x=t[i].tag; if(!x) return;
    	t[L].sumi=t[L].r-t[L].l+1-t[L].sumi;
    	t[R].sumi=t[R].r-t[R].l+1-t[R].sumi;
    	t[L].tag^=1; t[R].tag^=1; t[i].tag=0;
    } //下传标记
    
    inline void change(int i,int l,int r) {
    	if(l<=t[i].l && t[i].r<=r) {
    		t[i].sumi=t[i].r-t[i].l+1-t[i].sumi;
    		t[i].tag^=1; return;
    	} pushdown(i); int mid=(t[i].l+t[i].r)>>1;
    	if(l<=mid) change(L,l,r);
    	if(r>mid) change(R,l,r);;
    	update(i);
    } //区间修改
    
    inline int query(int i,int l,int r) {
    	if(l<=t[i].l && t[i].r<=r) return t[i].sumi;
    	pushdown(i); int mid=(t[i].l+t[i].r)>>1,ans=0;
    	if(l<=mid) ans+=query(L,l,r);
    	if(r>mid) ans+=query(R,l,r);
    	return ans;
    } //区间询问
    
    int main(){
    	n=read(); m=read();
    	build_tree(1,1,n);
    	while(m--) {
    		int opt=read(),l=read(),r=read();
    		if(!opt) change(1,l,r);
    		else printf("%d
    ",query(1,l,r));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    【CF1009F】Dominant Indices(长链剖分优化DP)
    长链剖分学习笔记
    【洛谷5294】[HNOI2019] 序列(主席树维护单调栈+二分)
    【洛谷5286】[HNOI2019] 鱼(计算几何)
    【洛谷5292】[HNOI2019] 校园旅行(思维DP)
    【UVA1309】Sudoku(DLX)
    初学DLX
    【LOJ2461】「2018 集训队互测 Day 1」完美的队列(分块+双指针)
    【LOJ6062】「2017 山东一轮集训 Day2」Pair(线段树套路题)
    【LOJ6060】「2017 山东一轮集训 Day1 / SDWC2018 Day1」Set(线性基)
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12515374.html
Copyright © 2020-2023  润新知