题目: 
   Farmer John尝试通过和奶牛们玩益智玩具来保持他的奶牛们思维敏捷. 其中一个大型玩具是 牛栏中的灯. N (2 <= N <= 100,000) 头奶牛中的每一头被连续的编号为1..N, 站在一个 彩色的灯下面.刚到傍晚的时候,
所有的灯都是关闭的. 奶牛们通过N个按钮来控制灯的开关; 按第i个按钮可以 改变第i个灯的状态.奶牛们执行M (1 <= M <= 100,000)条指令, 每个指令都是两个整数中的一个(0 <= 指令号 <= 1). 第1种指令(用0表示)包含两个数字S_i和E_i (1 <= S_i <= E_i <= N), 它们表示起始开关 和终止开关. 奶牛们只需要把从S_i到E_i之间的按钮都按一次, 就可以完成这个指令. 第2种指令(用1表示)同样包含两个数字S_i和E_i (1 <= S_i <= E_i
<= N), 不过这种指令 是询问从S_i到E_i之间的灯有多少是亮着的. 帮助FJ确保他的奶牛们可以得到正确的答案. 

这是我做的第一道线段树的题目,以前只了解过模板——下面我就来介绍一下。

-------------------------------------------线段树的介绍-------------------------------------------------------

线段树是什么?就是一种二叉树结构,而且它的每一个结点代表一条线段的和。

以下是百度百科的详细解释:

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
线段树至少支持下列操作:
Insert(t,x):将包含在区间 int 的元素 x 插入到树t中;
Delete(t,x):从线段树 t 中删除元素 x;
Search(t,x):返回一个指向树 t 中元素 x 的指针。

一般线段树我都是用堆来储存的,即a[i]的左儿子是a[i*2],右儿子是a[i*2+1]。

线段树快在哪里呢?他对于每个结点的修改和访问都是log(n)的效率。

相信到这里都很好理解,下面介绍一种更加强大的思想:lazy思想。

如果我要将a--b的区间的每一个值都加上p,那么效率就是(b-a+1)log(n)。有没有更快的方法呢?

通过研究,我们发现:如果它只修改而并不访问,我们岂不是不用更新了!

于是我们可以再开一个域cnt,表示目前有多少次累加被积压着。每次处理到k结点时(注意,这个处理包括访问和修改等),我们都把它积压的值给加上,然后传给它的儿子。此时它的儿子不必处理,直到要处理到它的儿子为止。

下面是这强大的update代码:

void update(long k)
{
  if (a[k].cnt==0) return;
  a[k].sum+=(a[k].r-a[k].l+1)*a[k].cnt;
  a[k*2].cnt+=a[k].cnt;a[k*2+1].cnt+=a[k].cnt;
  a[k].cnt=0;
}

每次处理到k时,先做一遍update(k)再进行操作即可。

------------------------------------------------回到原题-------------------------------------------------------

这里可能相对简单一些,下面说一些与模板不太相同的细节。

(1)初始建树时,每个值都是0.

(2)在插入时,每次cnt累加1即可,这个1当然不是加上1,而是执行操作数。

(3)在update中,如果cnt是偶数,就可以直接清零(因为偶数次不是抵消了嘛!);如果是奇数,我们把它相反一下,并向下传1。

代码:

#include<stdio.h>
using namespace std;
const long maxn=100001;
struct tree
{
  long l,r;                       //l和r就是a[i]所表示的左右区间范围。
  long cnt,sum;                   //cnt是累加的操作次数,sum是a[i]表示的线段中开着的灯的数量。
}a[4*maxn];
long n,m,i,x,y,ok;
void build(long k,long l,long r)   //基础建树
{
  a[k].cnt=0;a[k].l=l;a[k].r=r;
  if (l==r) return;
  long mid=(l+r)/2;
  build(k*2,l,mid);build(k*2+1,mid+1,r);
}
void update(long k)       //update操作。
{
  if (a[k].cnt==0) return;
  if (a[k].cnt%2==1)
  {
    a[k].sum=a[k].r-a[k].l+1-a[k].sum;       //a[k]的区间是a[k].l到a[k].r,那么共有a[k].r-a[k].l+1个,这里取反。
    a[k*2].cnt+=1;a[k*2+1].cnt+=1;
    a[k].cnt=0;
  }
  else a[k].cnt=0;
}
void ins(long k,long l,long r)
{
  update(k);
  if (a[k].l>=l&&a[k].r<=r) {a[k].cnt+=1;return;}   //当前区域被覆盖
  long mid=(a[k].l+a[k].r)/2;                      //二分找被覆盖的区域。
  if (l<=mid) ins(k*2,l,r);
  if (r>mid) ins(k*2+1,l,r);
  update(k*2);update(k*2+1);        //对儿子的更新操作(很重要),因为下面一步要重新更新当前的值。
  a[k].sum=a[k*2].sum+a[k*2+1].sum;
}
long find(long k,long l,long r)             //这与ins大同小异。
{
  update(k);
  if (a[k].l>=l&&a[k].r<=r) return a[k].sum;
  long mid=(a[k].l+a[k].r)/2,o=0;
  if (l<=mid) o+=find(k*2,l,r);
  if (r>mid) o+=find(k*2+1,l,r);
  update(k*2);update(k*2+1);
  a[k].sum=a[k*2].sum+a[k*2+1].sum;
  return o;
}
int main()
{
    //freopen("lites.in","r",stdin);
    //freopen("lites.out","w",stdout);
    scanf("%ld%ld",&n,&m);
    build(1,1,n);
    for (i=1;i<=m;i++)
    {
      scanf("%ld%ld%ld",&ok,&x,&y);
      if (ok==0) ins(1,x,y);
      else {long ans=find(1,x,y);printf("%ld\n",ans);}
    }
    //scanf("%ld",&n,&m);
    return 0;
}

usaco 2008 月赛 lites 开关灯 题解的更多相关文章

  1. usaco 2002 月赛 Fiber Communications 题解

    Description Farmer John wants to connect his N (1 <= N <= 1,000) barns (numbered 1..N) with a ...

  2. bzoj usaco 金组水题题解(1)

    UPD:我真不是想骗访问量TAT..一开始没注意总长度写着写着网页崩了王仓(其实中午的时候就时常开始卡了= =)....损失了2h(幸好长一点的都单独开了一篇)....吓得赶紧分成两坨....TAT. ...

  3. 1230: [Usaco2008 Nov]lites 开关灯

    1230: [Usaco2008 Nov]lites 开关灯 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1162  Solved: 589[Sub ...

  4. Code[VS]1690 开关灯 题解

    Code[VS]1690 开关灯 题解     时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond   题目描述 Description:     YYX家门前 ...

  5. BZOJ 1230: [Usaco2008 Nov]lites 开关灯( 线段树 )

    线段树.. --------------------------------------------------------------------------------- #include< ...

  6. usaco 2002 月赛 Chores 题解

    Description Farmer John's family pitches in with the chores during milking, doing all the chores as ...

  7. USACO全部月赛及GateWay数据

    月赛: 以07年open为例,网站如下 http://contest.usaco.org/OPEN07 其他的格式是http://contest.usaco.org/月份(月份的英文前三位,比如1月是 ...

  8. ZOJ Monthly, June 2014 月赛BCDEFGH题题解

    比赛链接:点击打开链接 上来先搞了f.c,,然后发现状态不正确,一下午都是脑洞大开,, 无脑wa,无脑ce...一样的错犯2次.. 硬着头皮搞了几发,最后20分钟码了一下G,不知道为什么把1直接当成不 ...

  9. USACO section 1.1 C++题解

    USACO section1.1:DONE 2017.03.03 TEXT Submitting Solutions DONE 2017.03.04 PROB Your Ride Is Here [A ...

随机推荐

  1. Linux vi 命令详解

    vi共分为三种模式:分别是一般模式,编辑模式与命令行模式 一般模式:以vi打开一个文件就直接了一般模式(这是默认的模式) 编辑模式:在指令模式下输入的按键“i, I, o, O, a, A, r, R ...

  2. MySQL主从搭建

    主服务器配置 1.编辑配置文件 # 如果不存在,就手动创建一个 vim /etc/my.cnf 在配置文件加入如下值: [mysqld] # 唯一的服务辨识号,数值位于 1 到 2^32-1之间. # ...

  3. 【Netty】UDP广播事件

    一.前言 前面学习了WebSocket协议,并且通过示例讲解了WebSocket的具体使用,接着学习如何使用无连接的UDP来广播事件. 二.UDP广播事件 2.1 UDP基础 面向连接的TCP协议管理 ...

  4. 第39篇 免费博客github Pages绑定域名

    原文地址:http://blog.laofu.online/2017/06/02/how-bind-domain/ 网站已经有了,需要对网站来绑定一个自己的个性域名,本人是买了一个阿里云的域名,也就是 ...

  5. Python有哪些好用的语言翻译方法

    最近有个需求,要将几万条数据从日语翻译成中文.因为数据的获取和处理用的是python代码,所以想先尝试翻译部分也用python实现. 目前网上查到的翻译方法有百度.有道云以及谷歌翻译,下面会对这三个方 ...

  6. Linux OS共享文件

    背景: 相较于windows.unix等OS,Linux因为其开源.安全.稳定.性能优越等优点,已越来越受到互联网的青睐.而我们在学习和使用Linux也就会考虑到Linux机器和我们日常用的windo ...

  7. 《物联网框架ServerSuperIO教程》-19.设备驱动和OPC Client支持mysql、oracle、sqlite、sqlserver的持久化。v3.6.4版本发布

    19.设备驱动和OPC Client支持mysql.oracle.sqlite.sqlserver的持久化 19.1     概述 ServerSuperIO支持设备驱动和OPC Client采集的数 ...

  8. [转]tomcat部署

    转载博客原文地址:  http://www.cnblogs.com/xing901022/p/4463896.html 阅读目录 介绍 静态部署——在tomcat启动时部署 动态部署——在tomcat ...

  9. 两个同级div等高布局

    显示效果: css代码如下 .wrap{ overflow:hidden; } .left{ width:30%; background:#09C; } .right{ width:70%; back ...

  10. DDD理论学习系列(5)-- 统一建模语言

    DDD理论学习系列--案例及目录 1.引言 上一节讲解了领域模型,领域模型主要是将业务中涉及到的概念以面向对象的思想进行抽象,抽象出实体对象,确定实体所对应的方法和属性,以及实体之间的关系.然后将这些 ...