线段树专题(持续更新中...)

单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来

  • hdu1166 敌兵布阵

  • 线段树功能:update:单点增减 query:区间求和

  •   1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 using namespace std;
      5 
      6 #define LL(x) (x<<1)
      7 #define RR(x) (x<<1|1)
      8 #define MID(a,b) (a+((b-a)>>1))
      9 const int N=50005;
     10 
     11 struct node
     12 {
     13     int lft,rht;
     14     int sum;
     15     int mid()
     16     {
     17         return MID(lft,rht);
     18     }
     19 };
     20 
     21 int y[N],n;
     22 
     23 struct Segtree
     24 {
     25     node tree[N*4];
     26     void build(int lft,int rht,int rt)
     27     {
     28         tree[rt].lft=lft;
     29         tree[rt].rht=rht;
     30         tree[rt].sum=0;
     31         if(lft==rht) 
     32             tree[rt].sum=y[lft];
     33         else
     34         {
     35             int mid=tree[rt].mid();
     36             build(lft,mid,LL(rt));
     37             build(mid+1,rht,RR(rt));
     38             tree[rt].sum=tree[LL(rt)].sum+tree[RR(rt)].sum;
     39         }
     40     }
     41     void updata(int pos,int rt,int valu)
     42     {
     43         if(tree[rt].lft==tree[rt].rht) 
     44             tree[rt].sum+=valu;
     45         else
     46         {
     47             int mid=tree[rt].mid();
     48             if(pos<=mid) 
     49                 updata(pos,LL(rt),valu);
     50             else 
     51                 updata(pos,RR(rt),valu);
     52             tree[rt].sum=tree[LL(rt)].sum+tree[RR(rt)].sum;
     53         }
     54     }
     55     int query(int st,int ed,int rt)
     56     {
     57         int lft=tree[rt].lft,rht=tree[rt].rht;
     58         if(st<=lft&&rht<=ed) 
     59             return tree[rt].sum;
     60         else
     61         {
     62             int mid=tree[rt].mid();
     63             int sum1=0,sum2=0;
     64             if(st<=mid) 
     65                 sum1=query(st,ed,LL(rt));
     66             if(ed>mid) 
     67                 sum2=query(st,ed,RR(rt));
     68             return sum1+sum2;
     69         }
     70     }
     71 }seg;
     72 int main()
     73 {
     74     int t,t_cnt=0;
     75     scanf("%d",&t);
     76     while(t--)
     77     {
     78         int a,b;
     79         char str[10];
     80         scanf("%d",&n);
     81         for(int i=1; i<=n; i++) 
     82             scanf("%d",&y[i]);
     83         seg.build(1,n,1);
     84         printf("Case %d:\n",++t_cnt);
     85         while(1)
     86         {
     87             scanf("%s",str);
     88             if(strcmp(str,"End")==0) 
     89                 break;
     90             scanf("%d%d",&a,&b);
     91             if(strcmp(str,"Add")==0) 
     92                 seg.updata(a,1,b);
     93             else if(strcmp(str,"Sub")==0) 
     94                 seg.updata(a,1,-b);
     95             else 
     96                 printf("%d\n",seg.query(a,b,1));
     97         }
     98     }
     99     return 0;
    100 }
    View Code

     

 hdu1754 I Hate It
  线段树功能:update:单点替换 query:区间最值,只要在上题代码的基础上稍加修改就好了,节点存储的是区间最大值

  

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 #define LL(x) (x<<1)
 7 #define RR(x) (x<<1|1)
 8 #define MID(a,b) (a+((b-a)>>1))
 9 const int N=200005;
10 #define INF (1<<30)
11 struct node
12 {
13     int lft,rht;
14     int sum;
15     int mid()
16     {
17         return MID(lft,rht);
18     }
19 };
20 struct Segtree
21 {
22     node tree[N*4];
23     void build(int lft,int rht,int rt)
24     {
25         tree[rt].lft=lft;
26         tree[rt].rht=rht;
27         tree[rt].sum=-INF;
28         if(lft==rht)
29             scanf("%d",&tree[rt].sum);
30         else
31         {
32             int mid=tree[rt].mid();
33             build(lft,mid,LL(rt));
34             build(mid+1,rht,RR(rt));
35             tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum);
36         }
37     }
38     void updata(int pos,int rt,int valu)
39     {
40         int lft=tree[rt].lft,rht=tree[rt].rht;
41         if(tree[rt].lft==tree[rt].rht)
42             tree[rt].sum=valu;
43         else
44         {
45             int mid=tree[rt].mid();
46             if(pos<=mid)
47                 updata(pos,LL(rt),valu);
48             else
49                 updata(pos,RR(rt),valu);
50             tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum);
51         }
52     }
53     int query(int st,int ed,int rt)
54     {
55         int lft=tree[rt].lft,rht=tree[rt].rht;
56         if(st<=lft&&rht<=ed)
57             return tree[rt].sum;
58         else
59         {
60             int mid=tree[rt].mid();
61             int sum1=-INF,sum2=-INF;
62             if(st<=mid)
63                 sum1=query(st,ed,LL(rt));
64             if(ed>mid)
65                 sum2=query(st,ed,RR(rt));
66             return max(sum1,sum2);
67         }
68     }
69 } seg;
70 int main()
71 {
72     int n,m;
73     while(scanf("%d%d",&n,&m)!=EOF)
74     {
75         int a,b;
76         char str[10];
77         seg.build(1,n,1);
78         for(int i=0; i<m; i++)
79         {
80             scanf("%s",str);
81             scanf("%d%d",&a,&b);
82             if(str[0]=='Q')
83                 printf("%d\n",seg.query(a,b,1));
84             else
85                 seg.updata(a,1,b);
86         }
87     }
88     return 0;
89 }
View Code

 

 

 

  

hdu1394 Minimum Inversion Number
  题意:求改变后的最小逆序数
  思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
  线段树功能:update:单点增减 query:区间求和

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 #define LL(x) (x<<1)
 7 #define RR(x) (x<<1|1)
 8 #define MID(a,b) (a+((b-a)>>1))
 9 const int N=5005;
10 #define INF (1<<30)
11 struct node
12 {
13     int lft,rht;
14     int sum;
15     int mid()
16     {
17         return MID(lft,rht);
18     }
19 };
20 struct Segtree
21 {
22     node tree[N*4];
23     void build(int lft,int rht,int rt)
24     {
25         tree[rt].lft=lft;
26         tree[rt].rht=rht;
27         tree[rt].sum=0;
28         if(lft!=rht)
29         {
30             int mid=tree[rt].mid();
31             build(lft,mid,LL(rt));
32             build(mid+1,rht,RR(rt));
33         }
34     }
35     void updata(int pos,int rt)
36     {
37         int lft=tree[rt].lft,rht=tree[rt].rht;
38         if(lft==rht)
39             tree[rt].sum++;
40         else
41         {
42             int mid=tree[rt].mid();
43             if(pos<=mid)
44                 updata(pos,LL(rt));
45             else
46                 updata(pos,RR(rt));
47             tree[rt].sum=tree[LL(rt)].sum+tree[RR(rt)].sum;
48         }
49     }
50     int query(int st,int ed,int rt)
51     {
52         int lft=tree[rt].lft,rht=tree[rt].rht;
53         if(st<=lft&&rht<=ed)
54             return tree[rt].sum;
55         else
56         {
57             int mid=tree[rt].mid();
58             int sum1=0,sum2=0;
59             if(st<=mid)
60                 sum1=query(st,ed,LL(rt));
61             if(ed>mid)
62                 sum2=query(st,ed,RR(rt));
63             return sum1+sum2;
64         }
65     }
66 } seg;
67 int main()
68 {
69     int n;
70     int sum;
71     while(scanf("%d",&n)!=EOF)
72     {
73         sum=0;
74         int a[5001];
75         seg.build(0,n-1,1);
76         for(int i=1; i<=n; i++)
77         {
78             scanf("%d",&a[i]);
79             seg.updata(a[i],1);
80             if(a[i]!=n-1)
81                 sum+=seg.query(a[i]+1,n-1,1);
82         }
83         int minm=sum;
84         for(int i=1;i<=n;i++)
85         {
86             sum-=a[i];
87             sum+=n-a[i]-1;
88             minm=min(minm,sum);
89         }
90         printf("%d\n",minm);
91     }
92     return 0;
93 }
View Code

 

hdu2795 Billboard
  题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
  思路:因为最多只有二十万张海报,所以板的最大的长度不会超过二十万,但是要小心,如果板的长度小于h,我们还要用h来建树。起初在查询的时候并不直接去更新它,而是查询找出它的更新位置的后,再写个updata函数去更新,但是我们可以在查询到它的位置的时候,同时去更新当前点的剩余长度,然后回溯更新所有祖先区间。返回它的查询位置的时候,因为左儿子找到的位置是pos1,右儿子找到的位置是pos2,两者都赋初值为0,又因为每次查询只找出一个位置,也就是说pos1和pos2中只有一个被改变,另一个仍保持0,所以返回pos1+pos2是正确的。

  线段树功能:query:区间求最大值的位子

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 #define LL(x) (x<<1)
 7 #define RR(x) (x<<1|1)
 8 #define MID(a,b) (a+((b-a)>>1))
 9 const int N=200005;
10 #define INF (1<<30)
11 int h,w,n;
12 struct node
13 {
14     int lft,rht;
15     int sum;
16     int mid()
17     {
18         return MID(lft,rht);
19     }
20 };
21 struct Segtree
22 {
23     node tree[N*4];
24     void build(int lft,int rht,int rt)
25     {
26         tree[rt].lft=lft;
27         tree[rt].rht=rht;
28         tree[rt].sum=w;
29         if(lft!=rht)
30         {
31             int mid=tree[rt].mid();
32             build(lft,mid,LL(rt));
33             build(mid+1,rht,RR(rt));
34         }
35     }
36     int updata(int value,int rt)
37     {
38         int pos;
39         int lft=tree[rt].lft,rht=tree[rt].rht;
40         if(lft==rht)
41         {
42             tree[rt].sum-=value;
43             return lft;
44         }
45         else
46         {
47             if(tree[LL(rt)].sum>=value)
48                 pos=updata(value,LL(rt));
49             else
50                 pos=updata(value,RR(rt));
51             tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum);
52             return pos;
53         }
54     }
55     /*int query(int st,int ed,int rt)
56     {
57         int lft=tree[rt].lft,rht=tree[rt].rht;
58         if(st<=lft&&rht<=ed)
59             return tree[rt].sum;
60         else
61         {
62             int mid=tree[rt].mid();
63             int sum1=0,sum2=0;
64             if(st<=mid)
65                 sum1=query(st,ed,LL(rt));
66             if(ed>mid)
67                 sum2=query(st,ed,RR(rt));
68             return sum1+sum2;
69         }
70     }*/
71 } seg;
72 int main()
73 {
74     int p;
75     while(scanf("%d%d%d",&h,&w,&n)!=EOF)
76     {
77         h=min(h,n);
78         seg.build(1,h,1);
79         for(int i=0; i<n; i++)
80         {
81             scanf("%d",&p);
82             if(seg.tree[1].sum<p)
83                 puts("-1");
84             else
85                 printf("%d\n",seg.updata(p,1));
86         }
87     }
88     return 0;
89 }
View Code

 hdu4521 小明系列问题——小明序列

    题意:有多组测试数据,每组数据的n和d表示,有n个数,求间距大于d的最长上升序列。

  思路:线段树中叶子结点表示值为i的并且以其结束的最长上升序列是多少。每次转移的时候,查询比当前值小的最大值就可以了。

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 #define LL(x) (x<<1)
 7 #define RR(x) (x<<1|1)
 8 #define MID(a,b) (a+((b-a)>>1))
 9 const int N=1e5+5;
10 #define INF (1<<30)
11 int a[N],n;
12 int imx[N];//记录最大区间长度
13 struct node
14 {
15     int lft,rht;
16     int sum;
17     int mid()
18     {
19         return MID(lft,rht);
20     }
21 };
22 struct Segtree
23 {
24     node tree[N*4];
25     void build(int lft,int rht,int rt)
26     {
27         tree[rt].lft=lft;
28         tree[rt].rht=rht;
29         tree[rt].sum=0;
30         if(lft!=rht)
31         {
32             int mid=tree[rt].mid();
33             build(lft,mid,LL(rt));
34             build(mid+1,rht,RR(rt));
35         }
36     }
37     void updata(int pos,int rt,int value)
38     {
39         int lft=tree[rt].lft,rht=tree[rt].rht;
40         if(lft==rht)
41         {
42             tree[rt].sum=max(tree[rt].sum,value);
43         }
44         else
45         {
46             int mid=tree[rt].mid();
47             if(pos<=mid)
48                 updata(pos,LL(rt),value);
49             else
50                 updata(pos,RR(rt),value);
51             tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum);
52         }
53     }
54     int query(int st,int ed,int rt)
55     {
56         int lft=tree[rt].lft,rht=tree[rt].rht;
57         if(st<=lft&&rht<=ed)
58             return tree[rt].sum;
59         else
60         {
61             int mid=tree[rt].mid();
62             int sum1=0,sum2=0;
63             if(st<=mid)
64                 sum1=query(st,ed,LL(rt));
65             if(ed>mid)
66                 sum2=query(st,ed,RR(rt));
67             return max(sum1,sum2);
68         }
69     }
70 } seg;
71 int main()
72 {
73     int n,d;
74     while(scanf("%d%d",&n,&d)!=EOF)
75     {
76         int maxm=0;
77         int ans=0;
78         for(int i=0;i<n;i++)
79         {
80             scanf("%d",&a[i]);
81             maxm=max(maxm,a[i]);
82             imx[i]=0;
83         }
84         seg.build(0,maxm,1);
85         for(int i=0;i<n;i++)
86         {
87             if(i-d-1>=0)//间隔大于d
88                 seg.updata(a[i-d-1],1,imx[i-d-1]);
89             if(a[i]>0)
90                 imx[i]=seg.query(0,a[i]-1,1)+1;//每次转移的时候,查询比当前值小的最大值就可以了。
91             else
92                 imx[i]=1;
93             ans=max(ans,imx[i]);
94         }
95         printf("%d\n",ans);
96     }
97     return 0;
98 }
View Code

 

 

最近做多校做到一道线段树,刚开始不会做,看了题解才会的,弱!

hdu5316 magician

  题意:给定n个数,m次操作,操作有两种
    (1)0 a b:输出区间[a,b]中美丽序列的最大和,区间[a,b]的美丽序列:[a,b]的子序列,且相邻元素的下标奇偶性不同
    (2)1 a b:将下标为a的值改为b

 思路:

  最大和要求子序列的相邻元素下标奇偶性不同,即下标奇偶交替即可
  则共有四种情况:子序列的开始下标和结尾下标为
  奇奇(jj),奇偶(jo),偶偶(oo),偶奇(oj)
  jj=jo+jj,jj=jj+oj,jo=jo+jo,jo=jj+oo
  oo=oo+jo,oo=oj+oo,oj=oo+oj,oj=oj+oj
  根据左右孩子推父结点时需要区间合并:(合并左右孩子)
  父结点的jj为 max(lson.jj,rson.jj,lson.jo+rson.jj,lson.jj+rson.oj)
  同样其他三种情况类推
  查询时要注意同样是区间合并(合并ans和包含要查询的子区间的结点),并不是单纯的取最大值
  最后的最大和为 max(ans.jj,ans.jo,ans.oj,ans.oo);
  因为题中存在负数,题中涉及到求最大值,相关的初始化要初始为负很大的数

参考代码:

  1 #include<stdio.h>
  2 #include<algorithm>
  3 #include<string.h>
  4 #define inf 0xffffffffffff
  5 using namespace std;
  6 #define LL(x) (x<<1)
  7 #define RR(x) (x<<1|1)
  8 #define MID(a,b) (a+((b-a)>>1))
  9 typedef long long LL;
 10 const int N=100001;
 11 int n,m;
 12 LL a[N+10];
 13 struct node
 14 {
 15     LL jj,jo,oj,oo;
 16 } tt;
 17 struct Segtree
 18 {
 19     node tree[N*4];
 20     void pushup(node& t,node lson,node rson)
 21     {
 22         //更新jo
 23         t.jo=max(lson.jo,rson.jo);
 24         t.jo=max(t.jo,lson.jo+rson.jo);
 25         t.jo=max(t.jo,lson.jj+rson.oo);
 26         //更新jj
 27         t.jj=max(lson.jj,rson.jj);
 28         t.jj=max(t.jj,lson.jo+rson.jj);
 29         t.jj=max(t.jj,lson.jj+rson.oj);
 30         //更新oo
 31         t.oo=max(lson.oo,rson.oo);
 32         t.oo=max(t.oo,lson.oo+rson.jo);
 33         t.oo=max(t.oo,lson.oj+rson.oo);
 34         //更新oj
 35         t.oj=max(lson.oj,rson.oj);
 36         t.oj=max(t.oj,lson.oo+rson.jj);
 37         t.oj=max(t.oj,lson.oj+rson.oj);
 38     }
 39     void build(int l,int r,int rt)
 40     {
 41         if(l==r)
 42         {
 43             tree[rt].jo=tree[rt].oj=-inf;
 44             tree[rt].oo=tree[rt].jj=-inf;
 45             if(l&1)
 46             {
 47                 tree[rt].jj=a[l];
 48             }
 49             else
 50             {
 51                 tree[rt].oo=a[l];
 52             }
 53             return;
 54         }
 55         int mid=(l+r)/2;
 56         build(l,mid,LL(rt));
 57         build(mid+1,r,RR(rt));
 58         pushup(tree[rt],tree[rt<<1],tree[rt<<1|1]);
 59     }
 60     void updata(int l,int r,int rt,int pos,int valu)
 61     {
 62         if(l==r)
 63         {
 64             if(l&1)
 65                 tree[rt].jj=valu;
 66             else
 67                 tree[rt].oo=valu;
 68             return;
 69         }
 70         int mid=(l+r)>>1;
 71         if(pos<=mid)
 72             updata(l,mid,rt<<1,pos,valu);
 73         else
 74             updata(mid+1,r,rt<<1|1,pos,valu);
 75         pushup(tree[rt],tree[rt<<1],tree[rt<<1|1]);
 76     }
 77     void query(int l,int r,int rt,int st,int ed)
 78     {
 79         if(st<=l&&r<=ed)
 80         {
 81             pushup(tt,tt,tree[rt]);
 82             return;
 83         }
 84         int mid=(l+r)/2;
 85         if(st<=mid)
 86             query(l,mid,LL(rt),st,ed);
 87         if(mid<ed)
 88             query(mid+1,r,RR(rt),st,ed);
 89     }
 90 } seg;
 91 int main()
 92 {
 93     int T;
 94     scanf("%d",&T);
 95     while(T--)
 96     {
 97         scanf("%d%d",&n,&m);
 98         for(int i=1; i<=n; i++)
 99             scanf("%I64d",&a[i]);
100         seg.build(1,n,1);
101         for(int i=0; i<m; i++)
102         {
103             int op,a,b;
104             scanf("%d%d%d",&op,&a,&b);
105             if(op)
106             {
107                 seg.updata(1,n,1,a,b);
108             }
109             else
110             {
111                 tt.oo=tt.oj=tt.jo=tt.jj=-inf;
112                 seg.query(1,n,1,a,b);
113                 long long maxm=tt.oo;
114                 maxm=max(maxm,tt.oj);
115                 maxm=max(maxm,tt.jj);
116                 maxm=max(maxm,tt.jo);
117                 printf("%I64d\n",maxm);
118             }
119         }
120     }
121     return 0;
122 }
View Code

 

好几天没做线段树了,回家前就练线段树了!

今天做的这道题很巧妙,只要了解做法就很好敲了。

poj2828 BuyTickets

  题意:有N个人排队,每一个人都有一个val来对应,每一个后来人都会插入当前队伍的某一个位置pos。要求把队伍最后的状态输出。

   思路:首先,我们想到最后那个人n的位置肯定是固定的了,所以,我们要求第n-1个人的位置就要在第n个人位置固定的基础上再找第pos[n-1]个空位。

  以此类推,我们可以知道要得到最终状态,就要从后往前定位,先建树,树的每个节点存左右区间和该区间内的空位数,然后开始找位置,找到对应的位置,将该位置的空位数置为0,然后要向上更新树。。。是不是很简单~。~

  参考代码:

  

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 using namespace std;
 5 #define LL(x) (x<<1)
 6 #define RR(x) (x<<1|1)
 7 #define MID(a,b) (a+((b-a)>>1))
 8 int n;
 9 int pos[200005],val[200005],res[200005];
10 struct node
11 {
12     int lft,rht,valu;
13     int mid()
14     {
15         return MID(lft,rht);
16     }
17 };
18 struct Segtree
19 {
20     node tree[200005*4];
21     void pushup(int rt)
22     {
23         tree[rt].valu=tree[LL(rt)].valu+tree[RR(rt)].valu;
24     }
25     void build(int l,int r,int rt)
26     {
27         tree[rt].lft=l;
28         tree[rt].rht=r;
29         tree[rt].valu=r-l+1;
30         if(l!=r)
31         {
32             int mid=tree[rt].mid();
33             build(l,mid,LL(rt));
34             build(mid+1,r,RR(rt));
35         }
36     }
37     int query(int p,int rt)
38     {
39         int lft=tree[rt].lft;
40         int rht=tree[rt].rht;
41         if(lft==rht)
42         {
43             tree[rt].valu=0;
44             return lft;
45         }
46         else
47         {
48             int pos;
49             if(tree[LL(rt)].valu>=p)
50                 pos=query(p,LL(rt));
51             else
52                 pos=query(p-(tree[LL(rt)].valu),RR(rt));
53             pushup(rt);
54             return pos;
55         }
56     }
57 } seg;
58 int main()
59 {
60     while(scanf("%d",&n)!=EOF)
61     {
62         seg.build(0,n-1,1);
63         for(int i=0; i<n; i++)
64             scanf("%d%d",&pos[i],&val[i]);
65         for(int i=n-1; i>=0; i--)
66         {
67             int p=seg.query(pos[i]+1,1);
68             res[p]=val[i];
69         }
70         for(int i=0; i<n; i++)
71         {
72             if(i)
73             {
74                 printf(" %d",res[i]);
75             }
76             else
77                 printf("%d",res[i]);
78         }
79         printf("\n");
80     }
81     return 0;
82 }
View Code

 POJ 2481 Cows

  题意:有N头牛,每只牛有一个测试值[S,E],如果对于牛i和牛j来说,它们的测验值满足下面的条件则证明牛i比牛j强壮:Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj。现在已知每一头牛的测验值,要求输出每头牛有几头牛比其强壮。

  思路: 先将S从小到大排序,E从大到小排序,这样就可以保证在放入一个区间里,前面所以放下的区间的左端点都是小于等于它的。那么,这时候查询,有多少个区间的右端点是大于等于它的右端点的,就可以得出答案。
        需要注意的是,在查询的时候,如果当前区间和前面一个区间是完全相同的话,那么直接将前面的区间得到的答案赋给它就可以,不然,查询大于等于它的右端点的数目。

  如果在查询的时候,把相同的区间排除掉就可以了。

参考代码:

  

  1 #include <set>
  2 #include <map>
  3 #include <list>
  4 #include <queue>
  5 #include <stack>
  6 #include <string>
  7 #include <math.h>
  8 #include <time.h>
  9 #include <vector>
 10 #include <stdio.h>
 11 #include <sstream>
 12 #include <string.h>
 13 #include <stdlib.h>
 14 #include <iostream>
 15 #include <algorithm>
 16 using namespace std;
 17 /***************************************/
 18 typedef vector<int> VI;
 19 typedef vector<char> VC;
 20 typedef vector<string> VS;
 21 typedef set<int> SI;
 22 typedef set<string> SS;
 23 typedef map<int ,int> MII;
 24 typedef map<string,int> MSI;
 25 typedef pair<int,int> PII;
 26 typedef vector<PII> VII;
 27 typedef vector<VI > VVI;
 28 /***************************************/
 29 #ifdef _WIN32
 30 #define ll __int64
 31 #else
 32 #define ll long long
 33 #endif
 34 #define MID(a,b) (a+((b-a)>>1))
 35 #define mem(a,b) memset(a,b,sizeof(a))
 36 #define all(x)  (x).begin(), (x).end()
 37 #define sz(x) ((int)(x).size())
 38 #define PB push_back
 39 #define MP make_pair
 40 #define LL(x) ((x)<<1)
 41 #define RR(x) ((x)<<1|1)
 42 #define sqr(x) ((x)*(x))
 43 #define pn()  printf("\n")
 44 #define sqr(x) ((x)*(x))
 45 /***************************************/
 46 const int INF = 0x7f7f7f7f;
 47 const ll LINF = (1LL<<60);
 48 const double eps = 1e-8;
 49 const double PIE=acos(-1.0);
 50 const int dx[]= {0,-1,0,1};
 51 const int dy[]= {1,0,-1,0};
 52 const int fx[]= {-1,-1,-1,0,0,1,1,1};
 53 const int fy[]= {-1,0,1,-1,1,-1,0,1};
 54 /***************************************/
 55 void openfile()
 56 {
 57     freopen("data.in","rb",stdin);
 58     freopen("data.out","wb",stdout);
 59 }
 60 void Scan(int& res)
 61 {
 62     int flag=0;
 63     char ch;
 64     while(!(((ch=getchar())>='0'&&ch<='9')||ch=='-'))
 65         if(ch==EOF)
 66             res=INF;
 67     if(ch=='-')
 68         flag=1;
 69     else if(ch>='0'&&ch<='9')
 70         res=ch-'0';
 71     while((ch=getchar())>='0'&&ch<='9')
 72         res=res*10+ch-'0';
 73     res=flag?-res:res;
 74 }
 75 void Out(int a)
 76 {
 77     if(a>9)
 78         Out(a/10);
 79     putchar(a%10+'0');
 80 }
 81 void Out(ll a)
 82 {
 83     if(a>9)
 84         Out(a/10);
 85     putchar(a%10+'0');
 86 }
 87 /**********************The End OF The Template*****************/
 88 
 89 const int N=1e5+5;
 90 int res[N];
 91 struct cow
 92 {
 93     int s,e,id;
 94 } cows[N];
 95 struct node
 96 {
 97     int lft,rht,sum;
 98     int mid()
 99     {
100         return MID(lft,rht);
101     }
102 };
103 struct segtree
104 {
105     node tree[N*4];
106     void build(int lft,int rht,int rt)
107     {
108         tree[rt].lft=lft;
109         tree[rt].rht=rht;
110         tree[rt].sum=0;
111         if(lft!=rht)
112         {
113             int mid=tree[rt].mid();
114             build(lft,mid,LL(rt));
115             build(mid+1,rht,RR(rt));
116         }
117     }
118     int query(int st,int ed,int rt)
119     {
120         int lft=tree[rt].lft;
121         int rht=tree[rt].rht;
122         if(st<=lft&&ed>=rht)
123         {
124             return tree[rt].sum;
125         }
126         else
127         {
128             int sum=0;
129             int mid=tree[rt].mid();
130             if(st<=mid)
131                 sum+=query(st,ed,LL(rt));
132             if(ed>mid)
133                 sum+=query(st,ed,RR(rt));
134             return sum;
135         }
136     }
137     void updata(int pos,int ind)
138     {
139         tree[ind].sum++;
140         if(tree[ind].lft==tree[ind].rht)
141             return;
142         else
143         {
144             int mid=tree[ind].mid();
145             if(pos<=mid)
146                 updata(pos,LL(ind));
147             else
148                 updata(pos,RR(ind));
149         }
150     }
151 }seg;
152 int cmp(cow x,cow y)
153 {
154     return x.s<y.s||(x.s==y.s&&x.e>y.e);
155 }
156 int main()
157 {
158     int n;
159     int st,ed;
160     while(scanf("%d",&n)!=EOF&&n)
161     {
162         for(int i=0; i<n; i++)
163         {
164             scanf("%d%d",&st,&ed);
165             cows[i].s=st;
166             cows[i].e=ed;
167             cows[i].id=i;
168         }
169         sort(cows,cows+n,cmp);
170         seg.build(0,N,1);
171         for(int i=0; i<n; i++)
172         {
173             int p=cows[i].id,q=cows[i-1].id;
174             if(i&&cows[i].s==cows[i-1].s&&cows[i].e==cows[i-1].e)
175                 res[p]=res[q];
176             else res[p]=seg.query(cows[i].e,N,1);
177             seg.updata(cows[i].e,1);
178         }
179         for(int i=0; i<n; i++)
180         {
181             if(i)
182                 printf(" %d",res[i]);
183             else
184                 printf("%d",res[i]);
185         }
186         printf("\n");
187     }
188     return 0;
189 }
View Code

 

二、成段更新

       简单的说明:成段更新需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候。延迟标记的意思是:这个区间的左右儿子都需要被更新,但是当前区间已经更新了。

  

poj3468 A SimpleProblem with Integers(lazy思想详解)

  题意:给你N个数,Q个操作,操作有两种,‘Q a b ’是询问a~b这段数的和,‘C a b c’是把a~b这段数都加上c。

  思路:

    介绍Lazy思想:lazy-tag思想,记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。

    在此通俗的解释我理解的Lazy意思,比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,它的节点标记为rt,这时tree[rt].l == a && tree[rt].r == b 这时我们可以一步更新此时rt节点的sum[rt]的值,sum[rt] += c * (tree[rt].r - tree[rt].l + 1),注意关键的时刻来了,如果此时按照常规的线段树的update操作,这时候还应该更新rt子节点的sum[]值,而Lazy思想恰恰是暂时不更新rt子节点的sum[]值,到此就return,直到下次需要用到rt子节点的值的时候才去更新,这样避免许多可能无用的操作,从而节省时间 。

    下面通过具体的代码来说明之,在此先介绍下代码中的函数说明:

    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1

    宏定义左儿子lson和右儿子rson,貌似用宏的速度要慢。

    PushUp(rt):通过当前节点rt把值递归向上更新到根节点

    PushDown(rt):通过当前节点rt递归向下去更新rt子节点的值

    rt表示当前子树的根(root),也就是当前所在的结点

    下面直接来介绍update函数,Lazy操作主要就是用在这里:

void update(int c,int l,int r,int rt)//表示对区间[l,r]内的每个数均加c,rt是根节点
{
    if(tree[rt].l == l && r == tree[rt].r)
    {
        add[rt] += c;
        sum[rt] += (__int64)c * (r-l+1);
        return;
    }
    if(tree[rt].l == tree[rt].r) return;
    PushDown(rt,tree[rt].r - tree[rt].l + 1);
    int m = tree[rt].mid();
    if(r <= m) update(c,l,r,rt<<1);
    else if(l > m) update(c,l,r,rt<<1|1);
    else
    {
        update(c,l,m,rt<<1);
        update(c,m+1,r,rt<<1|1);
    }
    PushUp(rt);
}

    

if(tree[rt].l == l && r == tree[rt].r) 这里就是用到Lazy思想的关键时刻 正如上面说提到的,这里首先更新该节点的sum[rt]值,然后更新该节点具体每个数值应该加多少即add[rt]的值,注意此时整个函数就运行完了,直接return,而不是还继续向子节点继续更新,这里就是Lazy思想,暂时不更新子节点的值。

那么什么时候需要更新子节点的值呢?答案是在某部分update操作的时候需要用到那部分没有更新的节点的值的时候,这里可能有点绕口。这时就掉用PushDown()函数更新子节点的数值。

void PushDown(int rt,int m)
{
    if(add[rt])
    {
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        sum[rt<<1] += add[rt] * (m - (m>>1));
        sum[rt<<1|1] += add[rt] * (m>>1);
        add[rt] = 0;//更新后需要还原
    }
}

PushDown就是从当前根节点rt向下更新每个子节点的值,这段代码读者可以自己好好理解,这也是Lazy的关键。

下面再解释query函数,也就是用这个函数来求区间和

__int64 query(int l,int r,int rt)
{
    if(l == tree[rt].l && r == tree[rt].r)
    {
        return sum[rt];
    }
    PushDown(rt,tree[rt].r - tree[rt].l + 1);
    int m = tree[rt].mid();
    __int64 res = 0;
    if(r <= m) res += query(l,r,rt<<1);
    else if(l > m) res += query(l,r,rt<<1|1);
    else
    {
       res += query(l,m,rt<<1);
       res += query(m+1,r,rt<<1|1);
    }
    return res;
}

第一个if还是区间的判断和前面update的一样,到这里就可以知道答案了,所以就直接return。

接下来的查询就需要用到rt子节点的值了,由于我们用了Lazy操作,这段的数值还没有更新,因此我们需要调用PushDown函数去更新之,满足if(add[rt])就说明还没有更新。

参考代码:

  1 #include <set>
  2 #include <map>
  3 #include <list>
  4 #include <queue>
  5 #include <stack>
  6 #include <string>
  7 #include <math.h>
  8 #include <time.h>
  9 #include <vector>
 10 #include <stdio.h>
 11 #include <sstream>
 12 #include <string.h>
 13 #include <stdlib.h>
 14 #include <iostream>
 15 #include <algorithm>
 16 using namespace std;
 17 #define LL(x) (x<<1)
 18 #define RR(x) (x<<1|1)
 19 #define MID(a,b) (a+((b-a)>>1))
 20 const int N = 1e5 + 5;
 21 struct cow
 22 {
 23     int s, e, id;
 24 } cows[N];
 25 struct node
 26 {
 27     int lft, rht;
 28     long long sum,add;
 29     int mid()
 30     {
 31         return MID(lft, rht);
 32     }
 33 };
 34 struct segtree
 35 {
 36     node tree[N * 4];
 37     void pushup(int rt)
 38     {
 39         tree[rt].sum = tree[LL(rt)].sum + tree[RR(rt)].sum;
 40     }
 41     void pushdown(int rt, int m)
 42     {
 43         if (tree[rt].add)
 44         {
 45             tree[LL(rt)].add +=tree[rt].add;
 46             tree[RR(rt)].add += tree[rt].add;
 47             tree[LL(rt)].sum += tree[rt].add*(m-(m>>1));
 48             tree[RR(rt)].sum += tree[rt].add*(m >> 1);
 49             tree[rt].add = 0;
 50         }
 51     }
 52     void build(int lft, int rht, int rt)
 53     {
 54         tree[rt].lft = lft;
 55         tree[rt].rht = rht;
 56         tree[rt].sum = tree[rt].add = 0;
 57         if (lft == rht){
 58             scanf("%I64d", &tree[rt].sum);
 59             return;
 60         }
 61         else
 62         {
 63             int mid = tree[rt].mid();
 64             build(lft, mid, LL(rt));
 65             build(mid + 1, rht, RR(rt));
 66             pushup(rt);
 67         }
 68     }
 69     long long query(int st, int ed, int rt)
 70     {
 71         int lft = tree[rt].lft;
 72         int rht = tree[rt].rht;
 73         if (st == lft && ed == rht)
 74         {
 75             return tree[rt].sum;
 76         }
 77         pushdown(rt,rht-lft + 1);
 78         int mid = tree[rt].mid();
 79         long long res = 0;
 80         if (ed<= mid)
 81             res += query(st, ed, LL(rt));
 82         else if (st > mid)
 83             res += query(st, ed, RR(rt));
 84         else
 85         {
 86             res += query(st, mid, LL(rt));
 87             res += query(mid + 1, ed, RR(rt));
 88         }
 89         return res;
 90     }
 91     void update(int st, int ed, int rt, int add)
 92     {
 93         int lft = tree[rt].lft, rht = tree[rt].rht;
 94         if (st == lft && ed == rht)
 95         {
 96             tree[rt].add += add;
 97             tree[rt].sum += (long long)(rht - lft + 1) * add;
 98             return;
 99         }
100         if (tree[rt].lft == tree[rt].rht)
101             return;
102         pushdown(rt, rht - lft + 1);
103         int mid = tree[rt].mid();
104         if (ed <= mid)
105             update(st, ed, LL(rt), add);
106         else if (st > mid)
107             update(st, ed, RR(rt), add);
108         else
109         {
110             update(st, mid, LL(rt), add);
111             update(mid + 1, ed, RR(rt), add);
112         }
113         pushup(rt);
114     }
115 } seg;
116 int main()
117 {
118     int n, m;
119     scanf("%d%d", &n, &m);
120     seg.build(1, n, 1);
121     for (int i = 0; i < m; i++)
122     {
123         char op[2];
124         scanf("%s", op);
125         int a, b, c;
126         if (op[0] == 'Q')
127         {
128             scanf("%d%d", &a, &b);
129             printf("%I64d\n", seg.query(a, b, 1));
130         }
131         else
132         {
133             scanf("%d%d%d", &a, &b, &c);
134             seg.update(a, b, 1, c);
135         }
136     }
137     return 0;
138 }
View Code

 

hdu1698 Just a Hook

题意:给你T组数据,N个数(初始时每个数的值为1),M个操作,每个操作把区间[a,b]里的数更新为c,问最后这N个数的和是多少。

思路:比上题简单,在上题代码上稍加修改即可。

参考代码:

  1 #include <set>
  2 #include <map>
  3 #include <list>
  4 #include <queue>
  5 #include <stack>
  6 #include <string>
  7 #include <math.h>
  8 #include <time.h>
  9 #include <vector>
 10 #include <stdio.h>
 11 #include <sstream>
 12 #include <string.h>
 13 #include <stdlib.h>
 14 #include <iostream>
 15 #include <algorithm>
 16 using namespace std;
 17 #define LL(x) (x<<1)
 18 #define RR(x) (x<<1|1)
 19 #define MID(a,b) (a+((b-a)>>1))
 20 const int N = 1e5 + 5;
 21 struct cow
 22 {
 23     int s, e, id;
 24 } cows[N];
 25 struct node
 26 {
 27     int lft, rht;
 28     long long sum, add;
 29     int mid()
 30     {
 31         return MID(lft, rht);
 32     }
 33 };
 34 struct segtree
 35 {
 36     node tree[N * 4];
 37     void pushup(int rt)
 38     {
 39         tree[rt].sum = tree[LL(rt)].sum + tree[RR(rt)].sum;
 40     }
 41     void pushdown(int rt, int m)
 42     {
 43         if (tree[rt].add)
 44         {
 45             tree[LL(rt)].add = tree[rt].add;
 46             tree[RR(rt)].add = tree[rt].add;
 47             tree[LL(rt)].sum = tree[rt].add * (m - (m >> 1));
 48             tree[RR(rt)].sum = tree[rt].add * (m >> 1);
 49             tree[rt].add = 0;
 50         }
 51     }
 52     void build(int lft, int rht, int rt)
 53     {
 54         tree[rt].lft = lft;
 55         tree[rt].rht = rht;
 56         tree[rt].add = 0;
 57         if (lft == rht)
 58             tree[rt].sum = 1;
 59         else {
 60             int mid = tree[rt].mid();
 61             build(lft, mid, LL(rt));
 62             build(mid + 1, rht, RR(rt));
 63             pushup(rt);
 64         }
 65     }
 66     /*long long query(int st, int ed, int rt)
 67     {
 68         int lft = tree[rt].lft;
 69         int rht = tree[rt].rht;
 70         if (st == lft && ed == rht)
 71         {
 72             return tree[rt].sum;
 73         }
 74         pushdown(rt, rht - lft + 1);
 75         int mid = tree[rt].mid();
 76         long long res = 0;
 77         if (ed <= mid)
 78             res += query(st, ed, LL(rt));
 79         else if (st > mid)
 80             res += query(st, ed, RR(rt));
 81         else
 82         {
 83             res += query(st, mid, LL(rt));
 84             res += query(mid + 1, ed, RR(rt));
 85         }
 86         return res;
 87     }*/
 88     void update(int st, int ed, int rt, int c)
 89     {
 90         int lft = tree[rt].lft, rht = tree[rt].rht;
 91         if (st == lft && ed == rht)
 92         {
 93             tree[rt].add= c;
 94             tree[rt].sum = (long long)(rht - lft + 1) *c;
 95             return;
 96         }
 97         if (tree[rt].lft == tree[rt].rht)
 98             return;
 99         pushdown(rt, rht - lft + 1);
100         int mid = tree[rt].mid();
101         if (ed <= mid)
102             update(st, ed, LL(rt), c);
103         else if (st > mid)
104             update(st, ed, RR(rt), c);
105         else
106         {
107             update(st, mid, LL(rt), c);
108             update(mid + 1, ed, RR(rt), c);
109         }
110         pushup(rt);
111     }
112 } seg;
113 int main()
114 {
115     int T;
116     int cas=0;
117     scanf("%d", &T);
118     while (T--)
119     {
120         cas++;
121         int n, m;
122         scanf("%d%d", &n, &m);
123         seg.build(1, n, 1);
124         while (m--)
125         {
126             int a, b, c;
127             scanf("%d%d%d", &a, &b, &c);
128             seg.update(a, b, 1, c);
129         }
130         printf("Case %d: The total value of the hook is %I64d.\n",cas,seg.tree[1].sum);
131     }
132     return 0;
133 }
View Code

 

posted @ 2015-07-20 10:59  PJQOOO  阅读(950)  评论(1编辑  收藏  举报