zoukankan      html  css  js  c++  java
  • 洛谷 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;
    }
    
    
  • 相关阅读:
    1509 -- Glass Beads POJ
    1043: [HAOI2008]下落的圆盘
    面向对象的特性一:封装性
    java的值传递机制
    可变参数的形参方法
    方法的重载
    类及对象
    面向对象
    数组的常用算法问题
    数组的常见异常
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12515374.html
Copyright © 2011-2022 走看看