Vijos1448题解---线段树+括号法

描述

校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
K=1,K=1,读入l、r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同
K=2,读入l,r表示询问l~r之间能见到多少种树(l,r>0)

输入格式

第一行n,m表示道路总长为n,共有m个操作
接下来m行为m个操作

输出格式

对于每个k=2输出一个答案

 样例输入

5 4

1 1 3

2 2 5

1 2 4

2 3 5

样例输出

1
2
 
普通的暴力算法,植树的时间为O(n),查询的时间也为O(n),所以总体的时间复杂度为O(nm)。
这里介绍一种独特的想法---括号法。
在植树区间的左端点放一个左括号“(”,右端点放一个右括号“)”,使得植树时间为O(1)。
显而易见,查询的结果为右端点左边“(”的个数减去左端点左边“)”的个数,时间为O(n)。
2至5之间有2-1=1种树。
3至5之间有2-0=2种树。
为了进一步优化时间复杂度,我们使用线段树维护左右括号的数量,使时间降到log级。
献上代码:
 1 #include <cstdio>
 2 int n,m;
 3 struct node
 4 {
 5     int x,y;
 6     int a[3];//a[1]表示左括号的数量,a[2]表示右括号的数量。
 7 }t[300002];
 8 void init(int k,int l,int r)//初始化。
 9 {
10     t[k].x=l;
11     t[k].y=r;
12     if(l==r)return;
13     int mid=(l+r)/2;
14     init(2*k,l,mid);
15     init(2*k+1,mid+1,r);
16 }
17 void build(int k,int l,int op)
18 {
19     if(t[k].x<=l&&l<=t[k].y)//若在当前节点的范围内,括号数量加1。
20         t[k].a[op]++;
21     if(t[k].x==t[k].y)return;
22     int mid=(t[k].x+t[k].y)/2;
23     if(l<=mid)build(2*k,l,op);//查询左儿子。
24     if(l>=mid+1)build(2*k+1,l,op);//查询右儿子。
25 }
26 int find(int k,int l,int r,int op)//查找l到r内括号的个数。
27 {
28     int ans=0;
29     if(l<=t[k].x&&t[k].y<=r)return t[k].a[op];
30     int mid=(t[k].x+t[k].y)/2;
31     if(l<=mid)ans+=find(2*k,l,r,op);
32     if(r>=mid+1)ans+=find(2*k+1,l,r,op);
33     return ans;
34 }
35 int main()
36 {
37     int k,l,r,ans1,ans2;
38     scanf("%d%d",&n,&m);
39     init(1,1,n);
40     for(int i=1;i<=m;i++)
41     {
42         scanf("%d%d%d",&k,&l,&r);
43         if(k==1)
44         {
45             build(1,l,1);
46             build(1,r,2);
47         }
48         if(k==2)
49         {
50             ans1=ans2=0;
51             ans1=find(1,1,r,1);
52             if(l-1>=1)ans2=find(1,1,l-1,2);
53             printf("%d\n",ans1-ans2);
54         }
55     }
56     return 0;
57 }
posted @ 2016-08-16 14:24  c0per  阅读(458)  评论(0编辑  收藏  举报