線段樹總結


       寒假到現在這一個月斷斷續續的做了一些有關線段樹的題目,有句話確實說的沒錯:量的積累必將產生質的變化,可能是先學習了伸展樹吧,學習起線段樹感覺上手很多。一直ym各路大神,看着比自己牛X幾倍的人一直比我努力着,我不甘。雖然現在依然很菜,但是我會加油的。

 

一、單點更新

    1、 hdu1166 地兵布陣

         題目大意: 有N個工兵營,每個工兵營開始有a個人,然后有一些操作。Add i j 第i個工兵營加j個人 Sub 第i個工兵營減j個人,Q i j 詢問第i到第j個工兵營的總人數。

         解題思路:O(-1)

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 
 5 #define lz 2*u,l,mid
 6 #define rz 2*u+1,mid+1,r
 7 const int maxn=55555;
 8 int sum[2*maxn], a[maxn];
 9 
10 void build(int u, int l, int r)
11 {
12     sum[u]=0;
13     if(l==r)
14     {
15         sum[u]=a[l];
16         return ;
17     }
18     int mid=(l+r)>>1;
19     build(lz);
20     build(rz);
21     sum[u]=sum[2*u]+sum[2*u+1];
22 }
23 
24 void Update(int u, int l, int r, int pos, int c)
25 {
26     if(l==r)
27     {
28         sum[u]+=c;
29         return ;
30     }
31     int mid=(l+r)>>1;
32     if(pos<=mid) Update(lz,pos,c);
33     else Update(rz,pos,c);
34     sum[u]=sum[2*u]+sum[2*u+1];
35 }
36 
37 int Query(int u, int l, int r, int tl, int tr)
38 {
39     if(tl<=l&&r<=tr) return sum[u];
40     int mid=(l+r)>>1;
41     if(tr<=mid) return Query(lz,tl,tr);
42     else if(tl>mid) return Query(rz,tl,tr);
43     else
44     {
45         int t1=Query(lz,tl,mid);
46         int t2=Query(rz,mid+1,tr);
47         return t1+t2;
48     }
49 }
50 
51 int main()
52 {
53     int n, T, tcase=0;
54     cin >> T;
55     while(T--)
56     {
57         printf("Case %d:\n",++tcase);
58         cin >> n;
59         for(int i=1; i<=n; i++)
60             scanf("%d",a+i);
61         build(1,1,n);
62         char op[10];;
63         while(scanf("%s",op))
64         {
65             if(op[0]=='E') break;
66             else
67             {
68                 int l, r;
69                 scanf("%d%d",&l,&r);
70                 if(op[0]=='Q') printf("%d\n",Query(1,1,n,l,r));
71                 else
72                 {
73                     if(op[0]=='S') r*=-1;
74                     Update(1,1,n,l,r);
75                 }
76             }
77         }
78     }
79     return 0;
80 }

   

    2、hdu2795  billbord

     題目大意:有一塊板子,長寬分別為W,H,然后有n塊1*w海報,讓你把這n快海報貼在這塊板子上,盡量板子的上方貼,同一行盡量往板子的左邊貼。對於第i塊海報如果能夠貼下則輸出能貼在第幾行,否則輸出-1。

    解題思路: 設線段樹的操作長度為H,則對應的葉子節點的最大值為W。設每次找到符合要求的位置(板子最上最左,對應線段樹中最下最左)減去相應的w值。

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 
 5 #define lz 2*u,l,mid
 6 #define rz 2*u+1,mid+1,r
 7 const int maxn=200005;
 8 int tree[4*maxn];
 9 int t_max[4*maxn];
10 int n, m, max_w, nn;
11 
12 void build(int u, int l, int r)
13 {
14     tree[u]=max_w;
15     if(l==r)
16     {
17         return ;
18     }
19     int mid=(l+r)>>1;
20     build(lz);
21     build(rz);
22 }
23 
24 void Update(int u, int l, int r, int id, int w)
25 {
26     if(l==id&&id==r)
27     {
28         tree[u]-=w;
29         return ;
30     }
31     int mid=(l+r)>>1;
32     if(id<=mid)
33         Update(lz,id,w);
34     else Update(rz,id,w);
35     tree[u]=max(tree[2*u],tree[2*u+1]);  ///向上更新最大子區間段
36 
37 }
38 
39 int Query(int u, int l, int r, int w)
40 {
41     if(tree[u]<w) return -1;  ///只需要判斷此區間最大空余w是否大於w即可,節省好多時間
42     if(l==r)
43     {
44         return l;
45     }
46     int mid=(l+r)>>1;
47     if(tree[2*u]>=w)
48         return  Query(lz,w);
49     else
50         return  Query(rz,w);
51 }
52 
53 int main()
54 {
55     while(cin >> n >> max_w >> m)
56     {
57         nn=min(n,m);
58         build(1,1,nn);
59         while(m--)
60         {
61             int w;
62             scanf("%d",&w);
63             int id=Query(1,1,nn,w);
64             if(id==-1) puts("-1");
65             else
66             {
67                 printf("%d\n",id);
68                 Update(1,1,nn,id,w);
69             }
70         }
71     }
72 }

   

    3、hdu1754 I hate it

     題目大意:給你N個數,M個操作,操作分兩類。(1)"Q A B“,查詢區間[A,B]內的最大值。(2)"U A B",將第A個數的值改成B。

     解題思路:O(-1)

View Code
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 #define lz l , m , rt << 1
 6 #define rz m + 1 , r , rt << 1 | 1
 7 const int maxn = 222222;
 8 int maxx[maxn<<2];
 9 
10 void push_up(int rt)
11 {
12     maxx[rt] = max(maxx[rt<<1] , maxx[rt<<1|1]);
13 }
14 void build(int l,int r,int rt)
15 {
16     if (l == r)
17     {
18         scanf("%d",&maxx[rt]);
19         return ;
20     }
21     int m = (l + r) >> 1;
22     build(lz);
23     build(rz);
24     push_up(rt);
25 }
26 void update(int p,int sc,int l,int r,int rt)
27 {
28     if (l == r)
29     {
30         maxx[rt] = sc;
31         return ;
32     }
33     int m = (l + r) >> 1;
34     if (p <= m) update(p , sc , lz);
35     else update(p , sc , rz);
36     push_up(rt);
37 }
38 int query(int L,int R,int l,int r,int rt)
39 {
40     if (L <= l && r <= R)
41     {
42         return maxx[rt];
43     }
44     int m = (l + r) >> 1;
45     int ret = 0;
46     if (L <= m) ret = max(ret , query(L , R , lz));
47     if (R > m) ret = max(ret , query(L , R , rz));
48     return ret;
49 }
50 int main()
51 {
52     int n , m;
53     while (~scanf("%d%d",&n,&m))
54     {
55         build(1 , n , 1);
56         while (m --)
57         {
58             char op[2];
59             int a , b;
60             scanf("%s%d%d",op,&a,&b);
61             if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
62             else update(a , b , 1 , n , 1);
63         }
64     }
65     return 0;
66 }

 

    4、poj2828 Buy Tickets

     題目大意:有n個人排隊買票對應n個操作,每個操作有兩個值 pos val ,代表第i個人會插到第pos個人后面,他手上票的值為val。n個操作完后讓你輸出n個人排成一隊的總狀態。

     解題思路:題目轉了一個彎,不是讓你直接求第幾位的值是多少。但是其實道理是一樣的,從后往前開始排,每個人在他自己前面留pos個空位置,有人的位置不算,因為我們是倒着來的,后面的人肯定會把自己給擠掉的。

View Code
 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 
 5 #define lz 2*u,l,mid
 6 #define rz 2*u+1,mid+1,r
 7 const int maxn = 222222;
 8 int tree[4*maxn];
 9 int sum[4*maxn];
10 int a[maxn], b[maxn];
11 int n, cnt;
12 
13 void build(int u, int l, int r)
14 {
15     if(l==r)
16     {
17         sum[u]=1;
18         return ;
19     }
20     int mid=(l+r)>>1;
21     build(lz);
22     build(rz);
23     sum[u]=sum[2*u]+sum[2*u+1];
24 }
25 
26 void Update(int u, int l, int r, int pos, int val)
27 {
28     if(l==r)
29     {
30         tree[l]=val;
31         sum[u]=0;
32         return ;
33     }
34     int mid=(l+r)>>1;
35     if(sum[2*u]>=pos)    ///只需判斷前面有多少個空格
36           Update(lz,pos,val);
37     else
38     {
39         pos-=sum[2*u];
40         Update(rz,pos,val);
41     }
42     sum[u]=sum[2*u]+sum[2*u+1];
43 }
44 
45 int main()
46 {
47     while(cin >> n)
48     {
49         for(int i=0; i<n; i++)
50             scanf("%d%d",a+i,b+i);
51         build(1,1,n);
52         for(int i=n-1; i>=0; i--)  ///從后往前
53             Update(1,1,n,a[i]+1,b[i]);
54         printf("%d",tree[1]);
55         for(int i=2; i<=n; i++)
56             printf(" %d",tree[i]);
57         puts("");
58     }
59     return 0;
60 }

 

    5、poj2886 Who Gets the Most Candies?

     題目大意:n個小孩圍成一圈,他們被順時針編號為 1 到 n。每個小孩手中有一個數字非0的卡片,游戲從第 K 個小孩開始,他告訴其他小孩他卡片上的數字並離開這個圈,他卡片上的數字 A 表明了下一個離開的小孩,A大於0表示順時針小於0表示逆時針。游戲將直到所有小孩都離開,在游戲中,第 p 個離開的小孩將得到 F(p) 個糖果,F(p) 是 p 的約數的個數,問誰將得到最多的糖果,輸出最幸運的小孩的名字和他可以得到的糖果的數量。

    解題思路:因為題目只要求得到出幸運的孩子是誰並且他得到的糖果的數量,所以我們不必將前n的數都處理完,只需先建立反素數表(1-n中含素數最多的數),每次更新到n之前那個最大的反素數即可。

View Code
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 #define lz 2*u,l,mid
 8 #define rz 2*u+1,mid+1,r
 9 const int maxn = 500005;
10 int tree[4*maxn];
11 int pos;
12 
13 const int antiprime[]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,
14 332640,498960,554400,665280};
15 /// 反素數表, 反素數指1-n中含有素因子數最多的數
16 
17 const int factorNum[]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,
18 90,96,100,108,120,128,144,160,168,180,192,200,216,224};
19 /// 反素數對應的約數個數
20 
21 struct node
22 {
23     char name[15];
24     int val;
25 }f[maxn];
26 
27 void build(int u, int l, int r)
28 {
29     tree[u]=r-l+1;
30     if(l==r) return ;
31     int mid=(l+r)>>1;
32     build(lz);
33     build(rz);
34 }
35 
36 void Update(int u, int l, int r, int pp)
37 {
38      tree[u]--;
39      if(l==r)
40      {
41          pos=l;
42          return ;
43      }
44      int mid=(l+r)>>1;
45      if(tree[2*u]>=pp) Update(lz,pp);
46      else
47          Update(rz,pp-tree[2*u]);
48 }
49 
50 int main()
51 {
52     int n, k, &mod=tree[1];  ///mod隨tree[1]變,賦的是地址
53     while(cin >> n >> k)
54     {
55         for(int i=1; i<=n; i++)
56             scanf("%s%d",&f[i].name,&f[i].val);
57         int cnt=0;
58         while(cnt<35&&antiprime[cnt]<=n) cnt++;
59         cnt--;
60         pos=0;
61         f[pos].val=0;
62         build(1,1,n);
63         for(int i=0; i<antiprime[cnt]; i++)
64         {
65             if(f[pos].val>0)  k=((k+f[pos].val-2)%mod+mod)%mod+1;
66             else  k=((k+f[pos].val-1)%mod+mod)%mod+1;
67             Update(1,1,n,k);
68         }
69         printf("%s %d\n",f[pos].name,factorNum[cnt]);
70     }
71     return 0;
72 }

 

 

 二、成段更新

      說實話,當初我學這里的時候確實糾結了良久,有點抽象,沒事走在路上多想想就好了。成段更新需要用到延遲標記(或者說懶惰標記),簡單的說就是每次更新的時候不要更新到底,用延遲標記使得更新延遲到下次需要更新或者詢問到的時候再傳遞下去。延遲標記的意思是:這個區間的左右兒子都需要被更新,但是當前區間已經更新了。

   

     1、hdu 1698 Just a Hook

      題目大意:給你n個數(初始時每個數的值為1),m個操作,每個操作把區間[l,r]里的數更新為c,問最后這n個數的和是多少。

      解題思路:O(-1)

View Code
 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 
 5 const int maxn=100005;
 6 int tree[4*maxn];
 7 int flag[4*maxn];
 8 
 9 void down(int u, int l, int r)
10 {
11     if(flag[u])
12     {
13         int mid=(l+r)>>1;
14         tree[2*u]=(mid-l+1)*flag[u];
15         tree[2*u+1]=(r-mid)*flag[u];
16         flag[2*u]=flag[u];
17         flag[2*u+1]=flag[u];
18         flag[u]=0;
19     }
20 }
21 
22 void build(int u, int l, int r)
23 {
24     flag[u]=0;
25     if(l==r)
26     {
27         tree[u]=1;
28         return ;
29     }
30     int mid=(l+r)>>1;
31     build(u*2,l,mid);
32     build(u*2+1,mid+1,r);
33     tree[u]=tree[2*u]+tree[2*u+1];
34 }
35 
36 void update(int u, int l, int r, int tl, int tr, int val)
37 {
38     if(tl<=l&&r<=tr)
39     {
40         tree[u]=(r-l+1)*val;
41         flag[u]=val;
42         return ;
43     }
44     down(u,l,r);
45     int mid=(l+r)>>1;
46     if(tr<=mid)  update(2*u,l,mid,tl,tr,val);
47     else if(tl>mid) update(2*u+1,mid+1,r,tl,tr,val);
48     else
49     {
50         update(2*u,l,mid,tl,tr,val);
51         update(2*u+1,mid+1,r,tl,tr,val);
52     }
53     tree[u]=tree[2*u]+tree[2*u+1];
54 
55 }
56 
57 int getsum(int u, int l, int r, int tl, int tr)
58 {
59     if(tl<=l&&r<=tr)
60     {
61         return tree[u];
62     }
63     down(u,l,r);
64     int mid=(l+r)>>1;
65     if(tr<=mid) return getsum(2*u,l,mid,tl,tr);
66     else if(tl>mid) return getsum(2*u+1,mid+1,r,tl,tr);
67     else
68     {
69         int t1=getsum(2*u,l,mid,tl,tr);
70         int t2=getsum(2*u+1,mid+1,r,tl,tr);
71         return t1+t2;
72     }
73 }
74 
75 int main()
76 {
77     int  T, n, Q, tcase=0;
78     cin >> T;
79     while(T--)
80     {
81         cin >> n>> Q;
82         build(1,1,n);
83         while(Q--)
84         {
85             int l, r, val;
86             scanf("%d%d%d",&l,&r,&val);
87             update(1,1,n,l,r,val);
88         }
89         int ans=getsum(1,1,n,1,n);
90         printf("Case %d: The total value of the hook is %d.\n",++tcase,ans);
91     }
92     return 0;
93 }

   

     2、poj3468 A Simple Problem with Integers

     題目大意:給你n個附有起始值的數,然后m次詢問。C  a b val :表示區間[a,b]的數都加上一個val值,Q a b :詢問區間[a,b]的綜合。

     解題思路:O(-1)

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5 
  6 #define lz 2*u,l,mid
  7 #define rz 2*u+1,mid+1,r
  8 const int maxn=100005;
  9 __int64 a[maxn];     ///還要注意范圍
 10 __int64 tree[4*maxn];
 11 __int64 flag[4*maxn];
 12 
 13 void push_up(int u)
 14 {
 15     tree[u]=tree[2*u]+tree[2*u+1];
 16 }
 17 
 18 void push_down(int u, int l, int r)
 19 {
 20     if(flag[u])
 21     {
 22         int mid=(l+r)>>1;
 23         flag[2*u]+=flag[u];  ///!!!
 24         flag[2*u+1]+=flag[u];  ///!!!
 25         tree[2*u]+=flag[u]*(mid-l+1);
 26         tree[2*u+1]+=flag[u]*(r-mid);
 27         flag[u]=0;
 28     }
 29 }
 30 
 31 void build(int u, int l, int r)
 32 {
 33     flag[u]=0;
 34     if(l==r)
 35     {
 36         tree[u]=a[l];
 37         return ;
 38     }
 39     int mid=(l+r)>>1;
 40     build(lz);
 41     build(rz);
 42     push_up(u);
 43 }
 44 
 45 void Update(int u, int l, int r, int tl, int tr, __int64 val)
 46 {
 47     if(tl<=l&&r<=tr)
 48     {
 49         tree[u]+=val*(r-l+1);
 50         flag[u]+=val;  ///!!!加上一個值 並非重新賦新值
 51         return ;
 52     }
 53     push_down(u,l,r);
 54     int mid=(l+r)>>1;
 55     if(tr<=mid)
 56         Update(lz,tl,tr,val);
 57     else if(tl>mid)
 58          Update(rz,tl,tr,val);
 59     else
 60     {
 61         Update(lz,tl,mid,val);
 62         Update(rz,mid+1,tr,val);
 63     }
 64     push_up(u);
 65 }
 66 
 67 __int64 Query(int u, int l, int r, int tl, int tr)
 68 {
 69     if(tl<=l&&r<=tr)
 70     {
 71         return tree[u];
 72     }
 73     push_down(u,l,r);
 74     int mid=(l+r)>>1;
 75     if(tr<=mid)
 76         return Query(lz,tl,tr);
 77     else if(tl>mid)
 78          return Query(rz,tl,tr);
 79     else
 80     {
 81         __int64 t1=Query(lz,tl,mid);
 82         __int64 t2=Query(rz,mid+1,tr);
 83         return t1+t2;
 84     }
 85     push_up(u);
 86 }
 87 
 88 int main()
 89 {
 90     int n, m, l, r;
 91     while(cin >> n >> m)
 92     {
 93         for(int i=1; i<=n; i++)
 94             scanf("%I64d",a+i);
 95         build(1,1,n);
 96         while(m--)
 97         {
 98             char ch[5];
 99             __int64 val;
100             scanf("%s",ch);
101             if(ch[0]=='C')
102             {
103                 scanf("%d%d%I64d",&l,&r,&val);
104                 Update(1,1,n,l,r,val);
105             }
106             else
107             {
108                 scanf("%d%d",&l,&r);
109                 printf("%I64d\n",Query(1,1,n,l,r));
110             }
111         }
112     }
113     return 0;
114 }

 

    3、poj 2528 Mayor’s posters

     題目大意:首先給出一個n,然后按順序在牆上貼n張海報,海報可以相互覆蓋,后面貼的海報可以覆蓋前面貼的海報,問你最后能看見幾張不同的海報。

     解題思路: 操作區間比較大,這里我們需要進行離散化,說實話離散化確實比較簡單,但是對於這題就有學問了。

     先說說離散化:給你區間[1,10],[10,20],[20,100],[1000,10000000], 這里我們[2,9],[11,19],[21,99],[101,999],[1000000,+oo]這些區間我們其實都用不上,而且線段樹開這么大空間肯定是超內存的。所以我們取每個區間不重復的端點值進行映射,1,10,11,20,100,1000,100000分別映射到1,2,3,4,5,6,7上面去。

   但是上面普通的離散化藐視對這題沒有用(這題數據有些弱,所以看不出來,說實話我也是過了之后去傻崽空間打醬油看到的),下面的話引自傻崽的原創。

    給出下面兩個簡單的例子應該能體現普通離散化的缺陷:
    例子一:1-10 1-4 5-10
    例子二:1-10 1-4 6-10
普通離散化后都變成了[1,4][1,2][3,4]
線段2覆蓋了[1,2],線段3覆蓋了[3,4],那么線段1是否被完全覆蓋掉了呢?
例子一是完全被覆蓋掉了,而例子二沒有被覆蓋。  (這里就是我說的學問所在)

為了解決這種缺陷,我們可以在排序后的數組上加些處理,比如說[1,2,6,10]
如果相鄰數字間距大於1的話,在其中加上任意一個數字,比如加成[1,2,3,6,7,10],然后再做線段樹就好了.

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <map>
  5 #include <cmath>
  6 #include <algorithm>
  7 using namespace std;
  8 
  9 #define lz 2*u,l,mid
 10 #define rz 2*u+1,mid+1,r
 11 const int maxn=20005;
 12 int flag[4*maxn];
 13 int A[maxn], B[2*maxn];
 14 int visit[2*maxn];
 15 int ans=0;
 16 
 17 struct node
 18 {
 19     int l, r;
 20 }f[maxn];
 21 
 22 void build(int u, int l, int r)
 23 {
 24     flag[u]=0;
 25     if(l==r)  return ;
 26     int mid=(l+r)>>1;
 27     build(2*u,l,mid);
 28     build(2*u+1,mid+1,r);
 29 }
 30 
 31 void Update(int u, int l, int r, int tl, int tr, int c)
 32 {
 33     if(flag[u]) return ;  ///此段已經貼滿了海報,不必再查詢下去了
 34     if(tl<=l&&r<=tr)
 35     {
 36         flag[u]=1;  ///標記
 37         if(!visit[c])
 38         {
 39             ans++;
 40             visit[c]=1;
 41         }
 42         return ;
 43     }
 44     int mid=(l+r)>>1;
 45     if(tr<=mid) Update(lz,tl,tr,c);
 46     else if(tl>mid) Update(rz,tl,tr,c);
 47     else
 48     {
 49         Update(lz,tl,mid,c);
 50         Update(rz,mid+1,tr,c);
 51     }
 52     if(flag[2*u]&&flag[2*u+1]) flag[u]=1; ///!!標記上傳
 53 }
 54 
 55 int getid(int x, int n)
 56 {
 57     int l=1, r=n, mid;
 58     while(l<=r)
 59     {
 60         mid=(l+r)>>1;
 61         if(B[mid]==x) return mid;
 62         else if(B[mid]<x) l=mid+1;
 63         else r=mid-1;
 64     }
 65 }
 66 
 67 int main()
 68 {
 69     int T, n;
 70     cin >> T;
 71     while(T--)
 72     {
 73         cin >> n;
 74         memset(visit,0,sizeof(visit));
 75         int num=0;
 76         for(int i=0; i<n; i++)
 77         {
 78             scanf("%d%d",&f[i].l,&f[i].r);
 79             A[++num]=f[i].l;
 80             A[++num]=f[i].r;
 81         }
 82         sort(A+1,A+num+1);
 83         int ep=1;
 84         B[1]=A[1];
 85         for(int i=2; i<=num; i++)  ///離散化
 86             if(B[ep]!=A[i]) B[++ep]=A[i];
 87         for(int i=ep; i>1; i--)
 88             if(B[i]!=B[i-1]+1) B[++ep]=B[i-1]+1;
 89         sort(B+1,B+ep+1);
 90         build(1,1,ep);
 91         ans=0;
 92         for(int i=n-1; i>=0; i--)
 93         {
 94             int l=getid(f[i].l,ep);
 95             int r=getid(f[i].r,ep);
 96             Update(1,1,ep,l,r,i+1);
 97         }
 98         printf("%d\n",ans);
 99     }
100     return 0;
101 }

   

     4、poj3277 City Horizon

      題目大意:  平面上有[1,40000]個建築,每個建築有一個區間[Ai,Bi]表示它的跨度,Hi表示其高度。要求這n個建築的平面覆蓋面積。

      解題思路:和上一題差不多,只是多了一個求面積操作,數據范圍過大,同理也要用到離散化。

View Code
  1 #include <cstdio>
  2 #include <iostream>
  3 #include <cstring>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define lz 2*u,l,mid
  8 #define rz 2*u+1,mid+1,r
  9 const int maxn = 100005;
 10 typedef long long lld;
 11 lld  flag[4*maxn];
 12 lld A[maxn], B[maxn];
 13 lld sum;
 14 
 15 struct node
 16 {
 17     lld l, w, h;
 18 }f[maxn];
 19 
 20 bool cmp(node A, node B)
 21 {
 22     return A.h<B.h;
 23 }
 24 
 25 lld find(lld x, lld n)
 26 {
 27     lld l=1, r=n, mid;
 28     while(l<=r)
 29     {
 30         mid=(l+r)>>1;
 31         if(B[mid]==x) return mid;
 32         else if(B[mid]<x) l=mid+1;
 33         else r=mid-1;
 34     }
 35 }
 36 
 37 void push_down(int u)
 38 {
 39     if(flag[u])
 40     {
 41         flag[2*u]=flag[2*u+1]=flag[u];
 42         flag[u]=0;
 43     }
 44 }
 45 
 46 void build(int u, int l, int r)
 47 {
 48     flag[u]=0;
 49     if(l==r)
 50         return ;
 51     int mid=(l+r)>>1;
 52     build(lz);
 53     build(rz);
 54 }
 55 
 56 void Update(int u, int l, int r, int tl, int tr, lld c)
 57 {
 58     if(tl<=l&&r<=tr)
 59     {
 60         flag[u]=c;
 61         return ;
 62     }
 63     push_down(u);
 64     int mid=(l+r)>>1;
 65     if(tr<=mid) Update(lz,tl,tr,c);
 66     else if(tl>mid) Update(rz,tl,tr,c);
 67     else
 68     {
 69         Update(lz,tl,mid,c);
 70         Update(rz,mid+1,tr,c);
 71     }
 72 }
 73 
 74 void Query(int u, int l, int r)
 75 {
 76     if(l==r)
 77     {
 78         sum+=flag[u]*(B[l+1]-B[l]);
 79         return ;
 80     }
 81     push_down(u);
 82     int mid=(l+r)>>1;
 83     Query(lz);
 84     Query(rz);
 85 }
 86 
 87 int main()
 88 {
 89     int n;
 90     while(cin >> n)
 91     {
 92         int num=0;
 93         for(int i=1; i<=n; i++)
 94         {
 95             scanf("%d%d%d",&f[i].l,&f[i].w,&f[i].h);
 96             A[++num]=f[i].l;
 97             A[++num]=f[i].w;
 98         }
 99         sort(A+1,A+num+1);
100         sort(f+1,f+n+1,cmp);
101         int ep=1;
102         B[1]=A[1];
103         for(int i=2; i<=num; i++)
104             if(A[i]!=B[ep]) B[++ep]=A[i];
105         build(1,1,ep);
106         for(int i=1; i<=n; i++)
107         {
108             int l=find(f[i].l,ep);
109             int r=find(f[i].w,ep);
110             Update(1,1,ep,l,r-1,f[i].h);
111         }
112         sum=0;
113         Query(1,1,ep);
114         cout << sum <<endl;
115     }
116     return 0;
117 }

   

     5、poj3255 Help with Intervals

      題目大意:集合的基本操作,具體地說就是並、交、相對補和對稱差。五種操作 U’、‘I’、‘D’、‘C’和‘S’中的一個 加上一個區間(T)形式為(a,b)(a,b][a,b)[a,b]。

      解題思路:  U:把區間[l,r]覆蓋成1;      I:把區間把[-∞,l)(r,∞]覆蓋成0;     D:把區間[l,r]覆蓋成0。

                      C:把區間[-∞,l)(r,∞],再對[l,r]區間0/1異或一次。  S:區間[l,r]內0/1異或一次。 

     這里要注意的一點就是先覆蓋再異或 and 先異或再覆蓋的區別了,兩者是不同的。

View Code
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define lz l,m,rt<<1
 5 #define rz m+1,r,rt<<1|1
 6 using namespace std;
 7 
 8 const int mm=133333;
 9 int col[mm<<2],turn[mm<<2],cov[mm+5];
10 
11 void T(int rt)
12 {
13     if(col[rt])col[rt]^=3;
14     else turn[rt]^=1;
15 }
16 void pushdown(int rt)
17 {
18     if(col[rt])
19     {
20         col[rt<<1]=col[rt<<1|1]=col[rt];
21         col[rt]=turn[rt<<1]=turn[rt<<1|1]=0;
22     }
23     if(turn[rt])
24     {
25         T(rt<<1);
26         T(rt<<1|1);
27         turn[rt]=0;
28     }
29 }
30 void updata(int L,int R,int op,int l,int r,int rt)
31 {
32     if(L<=l&&R>=r)
33     {
34         if(op)col[rt]=op,turn[rt]=0;
35         else T(rt);
36         return;
37     }
38     pushdown(rt);
39     int m=(l+r)>>1;
40     if(L<=m)updata(L,R,op,lz);
41     if(R>m)updata(L,R,op,rz);
42 }
43 void query(int l,int r,int rt)
44 {
45     if(col[rt])
46     {
47         if(col[rt]<2)
48             for(int i=l; i<=r; ++i)cov[i]=1;
49         return;
50     }
51     if(l==r)return;
52     pushdown(rt);
53     int m=(l+r)>>1;
54     query(lz);
55     query(rz);
56 }
57 int main()
58 {
59     char op,l,r;
60     int i,a,b,flag;
61     updata(0,mm,2,0,mm,1);
62     while(~scanf("%c %c%d,%d%c\n",&op,&l,&a,&b,&r))
63     {
64         a=(a<<1)+(l=='(');
65         b=(b<<1)-(r==')');
66         if(a>b)
67         {
68             if(op=='I'||op=='C')updata(0,mm,2,0,mm,1);
69         }
70         else
71         {
72             if(op=='U')updata(a,b,1,0,mm,1);
73             if(op=='I'||op=='C')
74             {
75                 if(a>0)updata(0,a-1,2,0,mm,1);
76                 if(b<mm)updata(b+1,mm,2,0,mm,1);
77             }
78             if(op=='D')updata(a,b,2,0,mm,1);
79             if(op=='C'||op=='S')updata(a,b,0,0,mm,1);
80         }
81     }
82     memset(cov,0,sizeof(cov));
83     query(0,mm,1);
84     for(a=b=-1,flag=i=0; i<=mm; ++i)
85         if(cov[i])b=i,a=a<0?i:a;
86         else if(a>=0)
87         {
88             if(flag)printf(" ");
89             flag=1;
90             printf("%c%d,%d%c",a&1?'(':'[',a>>1,(b+1)>>1,b&1?')':']');
91             a=b=-1;
92         }
93     if(!flag)printf("empty set");
94     puts("");
95     return 0;
96 }

 

   

  三、區間合並

       這樣的問題一般都是問你區間中滿足條件最長的序列,你關鍵需要知道怎樣操作對線段樹左右兒子進行合並。

    這樣的問題一般都要定義三個數組lm(定義從區間左邊第一個點開始的滿足條件的序列), rm(定義以區間右邊最后一個點結束的滿足條件的序列), sm(定義整個區間滿足條件的最長序列)。

   

    1、hdu3308 LCIS

     題目大意:給你n個整數,有兩種操作,(1)"U A B",表示把第A個數變成B,"Q A B",表示查詢區間[A,B]的最長連續上升序列。

     解題思路:O(-1)

View Code
  1 #include <cstdio>
  2 #include <cmath>
  3 #include <iostream>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define lz 2*u,l,mid
  8 #define rz 2*u+1,mid+1,r
  9 const int maxn=100005;
 10 int a[maxn];
 11 
 12 struct node
 13 {
 14     int lm;  ///定義從左邊第一個點開始的LCIS
 15     int rm;  ///定義以右邊最后一個點結束的LCIS
 16     int sm;  ///定義整段最大的LCIS
 17 }tree[4*maxn];
 18 
 19 void push_up(int u, int l, int r)
 20 {
 21     tree[u].lm=tree[2*u].lm;
 22     tree[u].rm=tree[2*u+1].rm;
 23     tree[u].sm=max(tree[2*u].sm,tree[2*u+1].sm);
 24     int mid=(l+r)>>1;
 25     if(a[mid]<a[mid+1])
 26     {
 27         if(tree[2*u].lm==mid-l+1) tree[u].lm=tree[2*u].lm+tree[2*u+1].lm;
 28         if(tree[2*u+1].rm==r-mid)  tree[u].rm=tree[2*u].rm+tree[2*u+1].rm;
 29         int t=tree[2*u].rm+tree[2*u+1].lm;
 30         if(t>tree[u].sm)  tree[u].sm=t;
 31     }
 32 }
 33 
 34 void build(int u, int l, int r)
 35 {
 36     if(l==r)
 37     {
 38         tree[u].lm=tree[u].rm=tree[u].sm=1;
 39         return ;
 40     }
 41     int mid=(l+r)>>1;
 42     build(lz);
 43     build(rz);
 44     push_up(u,l,r);
 45 }
 46 
 47 void Update(int u, int l, int r, int p, int d)
 48 {
 49     if(l==r)
 50     {
 51         a[l]=d;  return ;
 52     }
 53     int mid=(l+r)>>1;
 54     if(p<=mid)  Update(lz,p,d);
 55     else    Update(rz,p,d);
 56     push_up(u,l,r);
 57 }
 58 
 59 int Query(int u, int l, int r, int tl, int tr)
 60 {
 61     if(tl<=l&&r<=tr)
 62     {
 63         return tree[u].sm;
 64     }
 65     int mid=(l+r)>>1;
 66     if(tr<=mid)  return Query(lz,tl,tr);
 67     else if(tl>mid) return Query(rz,tl,tr);
 68     else
 69     {
 70         int t1=Query(lz,tl,mid);
 71         int t2=Query(rz,mid+1,tr);
 72         int t=max(t1,t2);
 73         if(a[mid]<a[mid+1])
 74         {
 75             t1=min(tree[2*u].rm,mid-tl+1);  ///!!!
 76             t2=min(tree[2*u+1].lm,tr-mid);  ///!!!
 77             t1+=t2;
 78         }
 79         return max(t,t1);
 80     }
 81 }
 82 
 83 int main()
 84 {
 85     int n, m, tcase;
 86     cin >> tcase;
 87     while(tcase--)
 88     {
 89         cin >> n >> m;
 90         for(int i=1; i<=n; i++)
 91             scanf("%d",a+i);
 92         build(1,1,n);
 93         while(m--)
 94         {
 95             char ch[5];
 96             int l, r;
 97             scanf("%s%d%d",ch,&l,&r);
 98             if(ch[0]=='U')
 99             {
100                 l++;
101                 Update(1,1,n,l,r);
102             }
103             else
104             {
105                 l++, r++;
106                 int ans=Query(1,1,n,l,r);
107                 printf("%d\n",ans);
108             }
109         }
110     }
111     return 0;
112 }

 

    2、poj3667 Hotel  (此題代碼有詳細注釋,作為初學者可以參考代碼理解)

    題目大意:讓你對一個區間進行操作。輸入Q C 或者 Q C D。

    Q  ==1  輸入 C: 表示讓你求1-n中是否有連續的C個空hotel,如果有多個連續的C個空hotel,則取最左邊的,並輸出最左邊的第一間hotel標號。讓人住進去,那么這些空房就不能住人了。如果不存在連續的C個hotel,則輸出 0。

    Q==2   輸入 C D: 表示 從標號C到C+D-1的hotel的房間全部要退房,那么這么hotel都變成空。

   解題思路:記錄整段區間最長的空房間。

View Code
  1 #include <cstdio>
  2  #include <cmath>
  3  #include <iostream>
  4  #include <algorithm>
  5  using namespace std;
  6  
  7  #define lz 2*u,l,mid
  8  #define rz 2*u+1,mid+1,r
  9  const int maxn=50005;
 10  int  flag[4*maxn];   ///標記
 11  
 12  struct node
 13  {
 14      int lm; ///從左邊第一個點開始最長的連續空hotel
 15      int rm; ///以右邊最后一個結束的最長的連續空hotel
 16      int sm; ///整段區間最大的連續空hotel 
 17  } tree[4*maxn];
 18  
 19  void push_up(int u, int l, int r)   ///向上更新
 20  {
 21      tree[u].lm=tree[2*u].lm;          
 22      tree[u].rm=tree[2*u+1].rm;
 23      tree[u].sm=max(tree[2*u].sm,tree[2*u+1].sm);
 24      int mid=(l+r)>>1;
 25      if(tree[2*u].lm==mid-l+1) tree[u].lm+=tree[2*u+1].lm;  ///!!這里注意,當左孩子左邊連續的達到整個區間時,要加上右孩子的左邊區間
 26      if(tree[2*u+1].rm==r-mid) tree[u].rm+=tree[2*u].rm;   ///!!考慮右區間,同上
 27      int t=tree[2*u].rm+tree[2*u+1].lm;
 28      if(t>tree[u].sm) tree[u].sm=t;
 29  }
 30  
 31  void push_down(int u, int l, int r)  ///向下更新
 32  {
 33      if(flag[u]==-1) return ;
 34      if(flag[u])
 35      {
 36          flag[2*u]=flag[2*u+1]=flag[u];
 37          tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=0;
 38          tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=0;
 39          flag[u]=-1;
 40      }
 41      else
 42      {
 43          flag[2*u]=flag[2*u+1]=flag[u];
 44          int mid=(l+r)>>1;
 45          tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=mid-l+1;
 46          tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=r-mid;
 47          flag[u]=-1;
 48      }
 49  }
 50  
 51  void build(int u, int l, int r)  ///建樹
 52  {
 53      flag[u]=-1;
 54      if(l==r)
 55      {
 56          tree[u].lm=tree[u].rm=tree[u].sm=1;
 57          return ;
 58      }
 59      int mid=(l+r)>>1;
 60      build(lz);
 61      build(rz);
 62      push_up(u,l,r);
 63  }
 64  
 65  void Update(int u, int l, int r, int tl, int tr, int c)   ///更新操作
 66  {
 67      if(tl<=l&&r<=tr)
 68      {
 69          tree[u].sm=tree[u].lm=tree[u].rm=(c==1?0:r-l+1);
 70          flag[u]= c;  
 71          return ;
 72      }
 73      push_down(u,l,r);   ///再次遇見此段區間時,延遲標記同步向下更新
 74      int mid=(l+r)>>1;
 75      if(tr<=mid) Update(lz,tl,tr,c);
 76      else if(tl>mid) Update(rz,tl,tr,c);
 77      else
 78      {
 79          Update(lz,tl,mid,c);    ///注意區間分隔開,tl,tr跨越兩個左右區間
 80          Update(rz,mid+1,tr,c);
 81      }
 82      push_up(u,l,r);     ///遞歸的時候同步向上更新
 83  }
 84  
 85  int Query(int u, int l, int r, int num)   ///詢問操作
 86  {
 87      if(l==r)
 88          return l;
 89      push_down(u,l,r);     ///延遲標記向下傳遞
 90      int mid=(l+r)>>1;
 91      if(tree[2*u].sm>=num) return Query(lz,num);
 92      else if(tree[2*u].rm+tree[2*u+1].lm>=num&&tree[2*u].rm>=1) return mid-tree[2*u].rm+1;   ///滿足條件時,返回左邊rm連續的hotel第一個房間標號
 93      else
 94          return Query(rz,num);
 95  }
 96  
 97  int main()
 98  {
 99      int n, m;
100      while(~scanf("%d%d",&n,&m))
101      {
102          build(1,1,n);
103          while(m--)
104          {
105              int p, u, v;
106              scanf("%d",&p);
107              if(p==1)
108              {
109                  scanf("%d",&u);
110                  if(tree[1].sm<u)  ///特判一下是否有這么多個連續的空hotel,沒有則直接輸出,不用操作
111                  {
112                      puts("0"); continue;
113                  }
114                  int p=Query(1,1,n,u);
115                  printf("%d\n",p);
116                  Update(1,1,n,p,p+u-1,1);
117              }
118              else
119              {
120                  scanf("%d%d",&u,&v);
121                  Update(1,1,n,u,u+v-1,0);
122              }
123          }
124      }
125      return 0;
126  }

 

    3、hdu3397 Sequence operation

     題目大意:有n個數,可能為0或為1。接下來是m個操作,操作有5種類型。(1)“0 a b”,表示將區間[a,b]范圍內的數全部置0;(2)“1 a b”,表示將區間[a,b]內的數全部置1;(3)"2 a b",表示將區間[a,b]內的數0變成1,1變成0;(4)"3 a b",表示查詢[a,b]范圍內1的數;(5)"4 a b",表示查詢[a,b]范圍內最長的連續的1;

     解題思路:開一個結構體,記錄一個區間內0的個數sum0,1的個數sum1,lm0:從左端點開始最長的連續0,lm1:從左端點開始最長的連續1,rm0:以右端點結束最長的0,rm1:以右端點結束最長的1,sm0:整段區間最長的連續0,sm1:整段區間最長的連續1。

     這里也要注意先覆蓋再異或和先異或再覆蓋的區別。先異或再覆蓋會把異或標記給覆蓋掉,先覆蓋再異或則不能把之前的更新覆蓋掉。

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define lz 2*u,l,mid
  8 #define rz 2*u+1,mid+1,r
  9 const int maxn=100005;
 10 int a[maxn];
 11 int flag[4*maxn];
 12 
 13 struct node
 14 {
 15     int lm0, rm0, sm0;
 16     int lm1, rm1, sm1;
 17     int sum0, sum1;
 18 } tree[4*maxn];
 19 
 20 void Xor(int u)
 21 {
 22     swap(tree[u].sum1,tree[u].sum0);
 23     swap(tree[u].lm1,tree[u].lm0);
 24     swap(tree[u].rm1,tree[u].rm0);
 25     swap(tree[u].sm1,tree[u].sm0);
 26 
 27     if(flag[u]==0) flag[u]=3;
 28     else if(flag[u]==1) flag[u]=2;
 29     else if(flag[u]==2) flag[u]=1;
 30     else if(flag[u]==3) flag[u]=0;
 31 }
 32 
 33 void push_up(int u, int l, int r)
 34 {
 35     tree[u].sum1=tree[2*u].sum1+tree[2*u+1].sum1;
 36     tree[u].sum0=tree[2*u].sum0+tree[2*u+1].sum0;
 37     tree[u].lm0=tree[2*u].lm0;
 38     tree[u].lm1=tree[2*u].lm1;
 39     tree[u].rm0=tree[2*u+1].rm0;
 40     tree[u].rm1=tree[2*u+1].rm1;
 41     tree[u].sm0=max(tree[2*u].sm0,tree[2*u+1].sm0);
 42     tree[u].sm1=max(tree[2*u].sm1,tree[2*u+1].sm1);
 43     int mid=(l+r)>>1;
 44 
 45     if(tree[2*u].lm1==mid-l+1) tree[u].lm1+=tree[2*u+1].lm1;
 46     if(tree[2*u+1].rm1==r-mid) tree[u].rm1+=tree[2*u].rm1;
 47     int t=tree[2*u].rm1+tree[2*u+1].lm1;
 48     if(t>tree[u].sm1) tree[u].sm1=t;
 49 
 50     if(tree[2*u].lm0==mid-l+1) tree[u].lm0+=tree[2*u+1].lm0;
 51     if(tree[2*u+1].rm0==r-mid) tree[u].rm0+=tree[2*u].rm0;
 52     int h=tree[2*u].rm0+tree[2*u+1].lm0;
 53     if(h>tree[u].sm0) tree[u].sm0=h;
 54 }
 55 
 56 void push_down(int u, int l, int r)
 57 {
 58     if(flag[u])
 59     {
 60         int mid=(l+r)>>1;
 61         if(flag[u]==1)
 62         {
 63             tree[2*u].sum1=tree[2*u].lm1=tree[2*u].rm1=tree[2*u].sm1=0;
 64             tree[2*u].sum0=tree[2*u].lm0=tree[2*u].rm0=tree[2*u].sm0=mid-l+1;
 65 
 66             tree[2*u+1].sum1=tree[2*u+1].lm1=tree[2*u+1].rm1=tree[2*u+1].sm1=0;
 67             tree[2*u+1].sum0=tree[2*u+1].lm0=tree[2*u+1].rm0=tree[2*u+1].sm0=r-mid;
 68             flag[2*u]=flag[2*u+1]=1;
 69         }
 70         else if(flag[u]==2)
 71         {
 72             tree[2*u].sum1=tree[2*u].lm1=tree[2*u].rm1=tree[2*u].sm1=mid-l+1;
 73             tree[2*u].sum0=tree[2*u].lm0=tree[2*u].rm0=tree[2*u].sm0=0;
 74 
 75             tree[2*u+1].sum1=tree[2*u+1].lm1=tree[2*u+1].rm1=tree[2*u+1].sm1=r-mid;
 76             tree[2*u+1].sum0=tree[2*u+1].lm0=tree[2*u+1].rm0=tree[2*u+1].sm0=0;
 77             flag[2*u]=flag[2*u+1]=2;
 78         }
 79         else
 80         {
 81             Xor(2*u);
 82             Xor(2*u+1);
 83         }
 84         flag[u]=0;
 85     }
 86 }
 87 
 88 void build(int u, int l, int r)
 89 {
 90     flag[u]=0;
 91     if(l==r)
 92     {
 93         tree[u].sum1=tree[u].lm1=tree[u].rm1=tree[u].sm1=a[l]?1:0;
 94         tree[u].sum0=tree[u].lm0=tree[u].rm0=tree[u].sm0=a[l]?0:1;
 95         return ;
 96     }
 97     int mid=(l+r)>>1;
 98     build(lz);
 99     build(rz);
100     push_up(u,l,r);
101 }
102 
103 void Update(int u, int l, int r, int tl, int tr, int c)
104 {
105     if(tl<=l&&r<=tr)
106     {
107         if(c==1)
108         {
109             tree[u].sum1=tree[u].lm1=tree[u].rm1=tree[u].sm1=0;
110             tree[u].sum0=tree[u].lm0=tree[u].rm0=tree[u].sm0=r-l+1;
111             flag[u]=1;
112         }
113         else if(c==2)
114         {
115             tree[u].sum1=tree[u].lm1=tree[u].rm1=tree[u].sm1=r-l+1;
116             tree[u].sum0=tree[u].lm0=tree[u].rm0=tree[u].sm0=0;
117             flag[u]=2;
118         }
119         else    /// 這里要特別注意,對於異或操作不能把此前的標記給覆蓋掉,必須進行相應的更新才能保證向下延遲標記不出錯,這里錯了好久
120         {
121             Xor(u);
122         }
123         return ;
124     }
125     push_down(u,l,r);
126     int mid=(l+r)>>1;
127     if(tr<=mid) Update(lz,tl,tr,c);
128     else if(tl>mid) Update(rz,tl,tr,c);
129     else
130     {
131         Update(lz,tl,mid,c);
132         Update(rz,mid+1,tr,c);
133     }
134     push_up(u,l,r);
135 }
136 
137 int Query1(int u,int l, int r, int tl, int tr)
138 {
139     if(tl<=l&&r<=tr)
140     {
141         return tree[u].sum1;
142     }
143     push_down(u,l,r);
144     int mid=(l+r)>>1;
145     if(tr<=mid) return Query1(lz,tl,tr);
146     else if(tl>mid) return Query1(rz,tl,tr);
147     else
148     {
149         int t1=Query1(lz,tl,mid);
150         int t2=Query1(rz,mid+1,tr);
151         return t1+t2;
152     }
153 }
154 
155 int Query2(int u, int l, int r, int tl, int tr)
156 {
157     if(tl<=l&&r<=tr)
158     {
159         return tree[u].sm1;
160     }
161     push_down(u,l,r);
162     int mid=(l+r)>>1;
163     if(tr<=mid) return Query2(lz,tl,tr);
164     else if(tl>mid) return Query2(rz,tl,tr);
165     else
166     {
167         int t1=Query2(lz,tl,mid);
168         int t2=Query2(rz,mid+1,tr);
169         int  t=max(t1,t2);
170         t1=min(tree[2*u].rm1,mid-tl+1);
171         t2=min(tree[2*u+1].lm1,tr-mid);
172         t1+=t2;
173         return max(t,t1);
174     }
175 }
176 
177 int main()
178 {
179     int T, n, m;
180     cin >> T;
181     while(T--)
182     {
183         cin >> n >> m;
184         for(int i=0; i<n; i++)
185             scanf("%d",a+i);
186         build(1,0,n-1);
187         while(m--)
188         {
189             int op, l, r;
190             scanf("%d%d%d",&op,&l,&r);
191             op++;
192             if(op==1) Update(1,0,n-1,l,r,1);
193             else if(op==2) Update(1,0,n-1,l,r,2);
194             else if(op==3) Update(1,0,n-1,l,r,3);
195             else if(op==4)
196             {
197                 int sum=Query1(1,0,n-1,l,r);
198                 printf("%d\n",sum);
199             }
200             else
201             {
202                 int len=Query2(1,0,n-1,l,r);
203                 printf("%d\n",len);
204             }
205         }
206     }
207     return 0;
208 }

 

   4、hdu2871 Memory control

   題目大意: 給n個單元內存,有 四個操作:

      1、New x     找一段長為x的空區間段填滿。  (下面用0表示空單元,1表示非空單元。)

      2、Free x  釋放包含x的區間段

      3、 Get x 找到第x個區間段

      4、將整個區間都置為空。

   解題思路:可以說這題應該算是線段樹之區間操作里面比較好的經典題了。要注意的地方很多。對vector容器的用法又了解了很多。贊一個

View Code
  1 #include <cstdio>
  2 #include <vector>
  3 #include <iostream>
  4 using namespace std;
  5 
  6 #define  lz  2*u,l,mid
  7 #define  rz  2*u+1,mid+1,r
  8 const int maxn=50005;
  9 int flag[4*maxn];
 10 
 11 struct segment
 12 {
 13     int lm; // 從區間左端點開始的連續0個數;
 14     int rm; // 以區間右端點結束的連續0個數;
 15     int sm; // 整個區間最長連續的0個數;
 16 } tree[4*maxn];
 17 
 18 struct node
 19 {
 20     int s, d;
 21 };
 22 node tmp;
 23 vector<node>vt;
 24 
 25 void push_up(int u, int l, int r)
 26 {
 27     int mid= (l+r)>>1;
 28     tree[u].lm= tree[u*2].lm;
 29     tree[u].rm= tree[u*2+1].rm;
 30     tree[u].sm= max( tree[u*2].sm, tree[u*2+1].sm );
 31     if( tree[u*2].lm == mid-l+1 ) tree[u].lm += tree[u*2+1].lm;
 32     if( tree[u*2+1].rm == r-mid ) tree[u].rm += tree[u*2].rm;
 33     int t= tree[u*2].rm + tree[u*2+1].lm;
 34     if( t > tree[u].sm )  tree[u].sm= t;
 35 }
 36 
 37 void push_down(int u, int l, int r)
 38 {
 39     if(flag[u]>=0)
 40     {
 41         int mid=(l+r)>>1;
 42         flag[2*u]=flag[2*u+1]=flag[u];
 43         tree[2*u].lm= tree[2*u].rm= tree[2*u].sm= flag[u]?0:mid-l+1;
 44         tree[2*u+1].lm= tree[2*u+1].rm= tree[2*u+1].sm= flag[u]?0:r-mid;
 45         flag[u]=-1;
 46     }
 47 }
 48 
 49 void build(int u, int l, int r)
 50 {
 51     flag[u]=-1;
 52     if(l==r)
 53     {
 54         tree[u].lm= tree[u].rm= tree[u].sm= 1;
 55         return;
 56     }
 57     int mid= (l+r)>>1;
 58     build(lz);
 59     build(rz);
 60     push_up(u, l, r);
 61 }
 62 
 63 void Update(int u, int l, int r, int tl, int tr, int c)
 64 {
 65     if(tl<=l&&r<=tr)
 66     {
 67         tree[u].lm=tree[u].rm=tree[u].sm=c?0:r-l+1;
 68         flag[u]=c;
 69         return ;
 70     }
 71     push_down(u,l,r);
 72     int mid=(l+r)>>1;
 73     if(tr<=mid) Update(lz,tl,tr,c);
 74     else if(tl>mid) Update(rz,tl,tr,c);
 75     else
 76     {
 77         Update(lz,tl,mid,c);
 78         Update(rz,mid+1,tr,c);
 79     }
 80     push_up(u,l,r);
 81 }
 82 
 83 int  Query(int u, int l, int r, int p)
 84 {
 85     if(tree[u].sm==p&&r-l+1==p)   ///不僅要tree[u].sm==p而且要整個區間都被0覆蓋,否則會出錯
 86     {
 87         return l;
 88     }
 89     push_down(u,l,r);
 90     int mid= (l+r)>>1, t;
 91     if(p<=tree[2*u].sm)  return Query(lz,p);
 92     else if(tree[2*u].rm+tree[2*u+1].lm>=p) return mid-tree[2*u].rm+1;
 93     else return Query(rz,p);
 94 }
 95 
 96 int find(int tp)    
 97 {
 98     int l=0, r=vt.size()-1, mid, ans=-1;
 99     while(l<=r)
100     {
101         int mid=(l+r)>>1;
102         if(vt[mid].s<=tp)
103         {
104             ans=mid;
105             l=mid+1;
106         }
107         else r=mid-1;
108     }
109     return ans;
110 }
111 
112 int main()
113 {
114     int n, m;
115     while(cin >> n >> m)
116     {
117         build(1,1,n);
118         vt.clear();
119         while(m--)
120         {
121             char ch[6];
122             scanf("%s",ch);
123             if(ch[0]=='R')
124             {
125                 vt.clear();
126                 Update(1,1,n,1,n,0);
127                 puts("Reset Now");
128                 continue;
129             }
130             int op;
131             scanf("%d",&op);
132             if(ch[0]=='N')
133             {
134                 if(tree[1].sm>=op)
135                 {
136                     int st=Query(1,1,n,op);
137                     tmp.s=st, tmp.d=st+op-1;
138                     int id=find(tmp.s);
139                     vt.insert(vt.begin()+id+1,tmp); 
140                     
141                     printf("New at %d\n",tmp.s);
142                     Update(1,1,n,tmp.s,tmp.d,1);  ///!!!這里注意了,查詢完了還要進行相應的更新
143                 }
144                 else
145                     puts("Reject New");
146             }
147             else if(ch[0]=='G')
148             {
149                 if(vt.size()>=op)
150                     printf("Get at %d\n",vt[op-1].s);
151                 else puts("Reject Get");
152             }
153             else if(ch[0]=='F')
154             {
155                 int id=find(op);
156                 if(id==-1||vt[id].d<op) puts("Reject Free");
157                 else
158                 {
159                     Update(1,1,n,vt[id].s,vt[id].d,0);
160                     printf("Free from %d to %d\n",vt[id].s,vt[id].d);
161                     vt.erase(vt.begin()+id,vt.begin()+id+1); ///刪除區間[s,d)內的元素,左閉右開
162                 }
163             }
164         }
165         puts("");
166     }
167     return 0;
168 }

   

     5、hdu1540 Tunnel Warfare

     題目大意:有N個村子排成一條直線,每個村子都連接了它的左右兩個村子(除了最左邊和最右邊的外),有3種操作,(1)"D x",表示將第x個村子摧毀。(2)"Q x",表示查詢與第x個村子直接和間接相連的村子有多少個。(3)"R",表示將最早摧毀的村子復原。

     解題思路:O(-1)

View Code
  1 #include <cstdio>
  2 #include <vector>
  3 #include <map>
  4 #include <iostream>
  5 using namespace std;
  6 
  7 #define  lz  2*u,l,mid
  8 #define  rz  2*u+1,mid+1,r
  9 const int maxn=50005;
 10 int flag[4*maxn];
 11 int stack[maxn];
 12 int top;
 13 
 14 struct segment
 15 {
 16     int lm;
 17     int rm;
 18     int sm;
 19 } tree[4*maxn];
 20 
 21 
 22 void push_up(int u, int l, int r)
 23 {
 24     int mid= (l+r)>>1;
 25     tree[u].lm= tree[u*2].lm;
 26     tree[u].rm= tree[u*2+1].rm;
 27     tree[u].sm= max( tree[u*2].sm, tree[u*2+1].sm );
 28     if( tree[u*2].lm == mid-l+1 ) tree[u].lm += tree[u*2+1].lm;
 29     if( tree[u*2+1].rm == r-mid ) tree[u].rm += tree[u*2].rm;
 30     int t= tree[u*2].rm + tree[u*2+1].lm;
 31     if( t > tree[u].sm )  tree[u].sm= t;
 32 }
 33 
 34 void build(int u, int l, int r)
 35 {
 36     flag[u]=-1;
 37     if(l==r)
 38     {
 39         tree[u].lm= tree[u].rm= tree[u].sm= 1;
 40         return;
 41     }
 42     int mid= (l+r)>>1;
 43     build(lz);
 44     build(rz);
 45     push_up(u, l, r);
 46 }
 47 
 48 void Update(int u, int l, int r, int pos, int c)
 49 {
 50     if(l==r)
 51     {
 52         tree[u].lm=tree[u].rm=tree[u].sm=c;
 53         return ;
 54     }
 55     int mid=(l+r)>>1;
 56     if(pos<=mid) Update(lz,pos,c);
 57     else  Update(rz,pos,c);
 58     push_up(u,l,r);
 59 }
 60 
 61 int  Query(int u, int l, int r, int p)
 62 {
 63     if(tree[u].sm==r-l+1) return tree[u].sm;
 64     if(l==r) return 0;
 65     int mid=(l+r)>>1;
 66     if(mid-tree[2*u].rm+1<=p&&p<=mid+tree[2*u+1].lm)
 67         return tree[2*u].rm+tree[2*u+1].lm;
 68     else if(p<=mid)  return Query(lz,p);
 69     else return Query(rz,p);
 70 }
 71 
 72 int main()
 73 {
 74     int n, m, op;
 75     while(cin >> n >> m)
 76     {
 77         map<int,int>mp;
 78         top=0;
 79         build(1,1,n);
 80         while(m--)
 81         {
 82             char ch[5];
 83             scanf("%s",ch);
 84             if(ch[0]=='D')
 85             {
 86                 scanf("%d",&op);
 87                 Update(1,1,n,op,0);
 88                 stack[++top]=op;
 89             }
 90             else if(ch[0]=='R')
 91             {
 92                 if(top)  Update(1,1,n,stack[top--],1);
 93             }
 94             else
 95             {
 96                 scanf("%d",&op);
 97                 int ans=Query(1,1,n,op);
 98                 printf("%d\n",ans);
 99             }
100         }
101     }
102     return 0;
103 }

 

 

 四、掃描線

       這類題目一般都要用到離散化排序之類,把每個矩陣的上下邊先存儲起來,每條邊在我們意想看來都是一條掃描線,一條接一條的從下往上掃描(像接力賽一樣的),每次掃描到的區間覆蓋到的區間cover值會發生變化,但是n條掃描線只在總區間上變化,換句話說:覆蓋區間只在總區間上變化,總區間一直保持不變。  有點抽象,看下面的題理解吧。

   

     1、hdu1542 Atlantis (入門題)

      題目大意:給你n個矩形,求他們的總面積之和(覆蓋區域只算一次)。

      解題思路: 詳解請移步到here

View Code
  1 View Code 
  2  #include <iostream>
  3  #include <cstdio>
  4  #include <cstring>
  5  #include <algorithm>
  6  using namespace std;
  7  
  8  #define lz 2*u,l,mid
  9  #define rz 2*u+1,mid+1,r
 10  const int maxn=4222;
 11  double sum[maxn];
 12  int flag[maxn];
 13  double X[maxn];
 14  
 15  struct Node
 16  {
 17      double lx, rx, y;
 18      int s;
 19      Node(){};
 20      Node(double lx_, double rx_, double y_, int s_)
 21      {
 22          lx=lx_, rx=rx_, y=y_, s=s_;
 23      }
 24      bool operator <(const Node &S) const
 25      {
 26          return y<S.y;
 27      }
 28  }line[maxn];
 29  
 30  int find(double tmp, int n)
 31  {
 32      int l=1, r=n, mid;
 33      while(l<=r)
 34      {
 35          mid=(l+r)>>1;
 36          if(X[mid]==tmp) return mid;
 37          else if(X[mid]<tmp) l=mid+1;
 38          else r=mid-1;
 39      }
 40  }
 41  
 42  void push_up(int u, int l, int r)
 43  {
 44      if(flag[u]) sum[u]=X[r+1]-X[l];
 45      else if(l==r) sum[u]=0;
 46      else sum[u]=sum[2*u]+sum[2*u+1];
 47  }
 48  
 49  void Update(int u, int l, int r, int tl, int tr, int c)
 50  {
 51      if(tl<=l&&r<=tr)
 52      {
 53          flag[u]+=c;
 54          push_up(u,l,r);
 55          return ;
 56      }
 57      int mid=(l+r)>>1;
 58      if(tr<=mid) Update(lz,tl,tr,c);
 59      else if(tl>mid) Update(rz,tl,tr,c);
 60      else
 61      {
 62          Update(lz,tl,mid,c);
 63          Update(rz,mid+1,tr,c);
 64      }
 65      push_up(u,l,r);
 66  }
 67  
 68  int main()
 69  {
 70      int n,tcase=0;
 71      while(cin >> n,n)
 72      {
 73          int num=0;
 74          memset(flag,0,sizeof(flag));
 75          memset(sum,0,sizeof(sum));
 76          for(int i=0; i<n; i++)
 77          {
 78              double x1,x2,y1,y2;
 79              scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
 80              line[++num]=Node(x1,x2,y1,1);
 81              X[num]=x1;
 82              line[++num]=Node(x1,x2,y2,-1);
 83              X[num]=x2;
 84          }
 85          sort(X+1,X+num+1);
 86          sort(line+1,line+num+1);
 87          int k=1;
 88          for(int i=2; i<=num; i++)
 89              if(X[i]!=X[i+1]) X[++k]=X[i];
 90          double ans=0;
 91          for(int i=1; i<num; i++)
 92          {
 93              int l=find(line[i].lx,k);
 94              int r=find(line[i].rx,k)-1;
 95              Update(1,1,k,l,r,line[i].s);
 96              ans+=sum[1]*(line[i+1].y-line[i].y);
 97          }
 98          printf("Test case #%d\n",++tcase);
 99          printf("Total explored area: %.2lf\n\n",ans);
100      }
101      return 0;
102  }

   

     2、hdu1828 Picture 

      題目大意:給你多個矩形,求他們合並后的周長,被覆蓋的邊不能算進周長之內。

      解題思路: 其實周長並和面積並沒什么很大的區別,只不過周長並增加了判斷左右端點是否被覆蓋的標記 lbd 和rbd 數組, 以及numseg 數組 記錄連續區間段數。

      numseg : 一根掃描線掃描過去,會記錄有多少個連續的區間段,每個連續的區間段都有兩條相等的豎邊,而每次掃描過去豎邊長度都相等。

     參考文獻 :陳宏《數據結構的選擇與算法效率》。

View Code
  1 View Code 
  2  #include <iostream>
  3  #include <cstdio>
  4  #include <cstring>
  5  #include <algorithm>
  6  using namespace std;
  7  
  8  #define lz 2*u,l,mid
  9  #define rz 2*u+1,mid+1,r
 10  const int maxn=22222;
 11  int sum[4*maxn];              ///記錄被覆蓋區間的長度
 12  int lbd[4*maxn], rbd[4*maxn]; ///記錄左右端點是否被覆蓋
 13  int numseg[4*maxn];            ///記錄該區間連續的段數
 14  int flag[4*maxn];              /// 記錄該區間是否被覆蓋
 15  
 16  struct Node
 17  {
 18      int lx, rx, y, s;
 19      int lbd, rbd;
 20      Node() {};
 21      Node(int lx_, int rx_, int y_, int s_)
 22      {
 23          lx=lx_, rx=rx_, y=y_, s=s_;
 24      }
 25      bool operator <(const Node &S) const
 26      {
 27          if(y==S.y) return s>S.s;
 28          return y<S.y;
 29      }
 30  } line[maxn];
 31  
 32  void push_up(int u, int l, int r)
 33  {
 34      if(flag[u])
 35      {
 36          lbd[u]=1;
 37          rbd[u]=1;
 38          sum[u]=r-l+1;
 39          numseg[u]=2;
 40      }
 41      else if(l==r)
 42      {
 43          sum[u]=numseg[u]=lbd[u]=rbd[u]=0;
 44      }
 45      else
 46      {
 47          lbd[u]=lbd[2*u];
 48          rbd[u]=rbd[2*u+1];
 49          sum[u]=sum[2*u]+sum[2*u+1];
 50          numseg[u]=numseg[2*u]+numseg[2*u+1];
 51          if(rbd[2*u]&&lbd[2*u+1]) numseg[u]-=2;
 52      }
 53  }
 54  
 55  void Update(int u, int l, int r, int tl, int tr, int c)
 56  {
 57      if(tl<=l&&r<=tr)
 58      {
 59          flag[u]+=c;
 60          push_up(u,l,r);
 61          return ;
 62      }
 63      int mid=(l+r)>>1;
 64      if(tr<=mid) Update(lz,tl,tr,c);
 65      else if(tl>mid) Update(rz,tl,tr,c);
 66      else
 67      {
 68          Update(lz,tl,mid,c);
 69          Update(rz,mid+1,tr,c);
 70      }
 71      push_up(u,l,r);
 72  }
 73  
 74  int main()
 75  {
 76      int n;
 77      while(cin >> n)
 78      {
 79          int x1, x2, y1, y2;
 80          int num=0, lbd=10000, rbd=-10000;
 81          for(int i=0; i<n; i++)
 82          {
 83              scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
 84              line[++num]=Node(x1,x2,y1,1);
 85              line[++num]=Node(x1,x2,y2,-1);
 86              lbd=min(lbd,x1);
 87              rbd=max(rbd,x2);
 88          }
 89          sort(line+1,line+num+1);
 90          int ans=0, last=0;
 91          for(int i=1; i<=num; i++)
 92          {
 93              Update(1,lbd,rbd-1,line[i].lx,line[i].rx-1,line[i].s);
 94              ans+=numseg[1]*(line[i+1].y-line[i].y);
 95              ans+=abs(sum[1]-last);
 96              last=sum[1];
 97          }
 98          cout << ans <<endl;
 99      }
100      return 0;
101  }

 

     3、hdu3265 Posters

     題目大意:給你n張矩形海報,每張矩形海報在中間剪去一塊矩形區域后貼在窗戶上,問你貼完所有的海報之后窗戶被海報覆蓋的區域有多大。

     解題思路:其實這題和第一題差不多。對於每張剪出矩形東東的海報,我們可以這樣處理:挨着中間空矩形的兩條橫邊或者兩條豎邊畫兩條線,那么每張海報不就變成四個實心矩形了么,接下來的解法和第一題一樣。

    這里要注意的是x1==x2的處理,不然會導致RE。

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <iostream>
 5 using namespace std;
 6 
 7 #define  lz  2*u,l,mid
 8 #define  rz  2*u+1,mid+1,r
 9 const int maxn=50005;
10 typedef long long lld;
11 lld sum[4*maxn];
12 int flag[4*maxn];
13 
14 struct Node
15 {
16     int lx, rx, h, s;
17     Node(){}
18     Node(int lx_, int rx_, int h_, int s_)
19     {
20         lx=lx_, rx=rx_, h=h_, s=s_;
21     }
22     bool operator<(const Node &S)const
23     {
24         return h<S.h;
25     }
26 }line[8*maxn];
27 
28 void push_up(int u, int l, int r)
29 {
30     if(flag[u]) sum[u]=r-l+1;
31     else if(l==r) sum[u]=0;
32     else sum[u]=sum[2*u]+sum[2*u+1];
33 }
34 
35 void Update(int u, int l, int r, int tl, int tr, int c)
36 {
37     if(tl>tr) return ;  ///!!!注意 當x1==x2時,會導致RE,因為算區間的時候x2會減-1
38     if(tl<=l&&r<=tr)
39     {
40         flag[u]+=c;
41         push_up(u,l,r);
42         return ;
43     }
44     int mid=(l+r)>>1;
45     if(tr<=mid) Update(lz,tl,tr,c);
46     else if(tl>mid) Update(rz,tl,tr,c);
47     else
48     {
49         Update(lz,tl,mid,c);
50         Update(rz,mid+1,tr,c);
51     }
52     push_up(u,l,r);
53 }
54 
55 int main()
56 {
57     int n;
58     while(cin >> n,n)
59     {
60         memset(flag,0,sizeof(flag));
61         memset(sum,0,sizeof(sum));
62         int num=0;
63         int x1, y1, x2, y2, x3, y3, x4, y4;
64         int lbd=maxn, rbd=-1;
65         for(int i=0; i<n; i++)
66         {
67             scanf("%d%d%d%d%d%d%d%d",&x1,&y1,&x4,&y4,&x2,&y2,&x3,&y3);
68             line[++num]=Node(x1,x4,y1,1);
69             line[++num]=Node(x1,x4,y2,-1);
70             line[++num]=Node(x1,x4,y3,1);
71             line[++num]=Node(x1,x4,y4,-1);
72             line[++num]=Node(x1,x2,y2,1);
73             line[++num]=Node(x1,x2,y3,-1);
74             line[++num]=Node(x3,x4,y2,1);
75             line[++num]=Node(x3,x4,y3,-1);
76             lbd=min(x1,lbd);
77             rbd=max(x4,rbd);
78         }
79         sort(line+1,line+num+1);
80         lld ans=0;
81         for(int i=1; i<num; i++)
82         {
83             Update(1,lbd,rbd,line[i].lx,line[i].rx-1,line[i].s);
84             ans+=sum[1]*(lld)(line[i+1].h-line[i].h);
85         }
86         cout << ans <<endl;
87     }
88     return 0;
89 }

   

     4、poj2482 Stars in Your Window

     題目大意:給你一些星星的坐標以及這些星星的亮度(x y c),然后給你一個矩形的框框,長寬分別為W、H,讓你用這個框框去圍住一個矩形的區域,要求區域內的星星亮度總和最大,並且矩形邊框的星星不計入氣內。

     解題思路:  對於每顆星星我們都對它進行一次構造,構造一個(W-1)*(H-1)的矩形(為什么不用W*H,因為邊框的星星不算入內啦),讓星星對應相應的構造矩形區域的最左下角,題目不就可以轉化成求某點最大的覆蓋亮度了么。每次更新前,記錄總區間內某點覆蓋亮度的最大值,最后取得最大值。

     題目上半部分:很優美的情書。題目下半部分:很經典的掃描線構造題。贊一個。

View Code
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <iostream>
  5 using namespace std;
  6 
  7 #define  lz  2*u,l,mid
  8 #define  rz  2*u+1,mid+1,r
  9 const int maxn=50005;
 10 __int64 max_sum[4*maxn];
 11 __int64 flag[4*maxn];
 12 __int64 X[maxn];
 13 
 14 struct Node
 15 {
 16     __int64 lx, rx, h, s;
 17     Node() {}
 18     Node(__int64 lx_, __int64 rx_, __int64 h_, __int64 s_)
 19     {
 20         lx=lx_, rx=rx_, h=h_, s=s_;
 21     }
 22     bool operator<(const Node &S)const
 23     {
 24         if(h==S.h) return s>S.s; ///!!!注意這里,錯了無數次
 25         return h<S.h;
 26     }
 27 } line[2*maxn];
 28 
 29 void push_down(int u, int l, int r)
 30 {
 31     if(flag[u]!=0)
 32     {
 33         flag[2*u]+=flag[u];
 34         flag[2*u+1]+=flag[u];
 35         max_sum[2*u]+=flag[u];
 36         max_sum[2*u+1]+=flag[u];
 37         flag[u]=0;
 38     }
 39 }
 40 
 41 void Update(int u, int l, int r, int tl, int tr, int c)
 42 {
 43     if(tl>tr) return ;  ///!!!注意 當x1==x2時,會導致RE,因為算區間的時候x2會減-1
 44     if(tl<=l&&r<=tr)
 45     {
 46         flag[u]+=c;
 47         max_sum[u]+=c;
 48         return ;
 49     }
 50     push_down(u,l,r);
 51     int mid=(l+r)>>1;
 52     if(tr<=mid) Update(lz,tl,tr,c);
 53     else if(tl>mid) Update(rz,tl,tr,c);
 54     else
 55     {
 56         Update(lz,tl,mid,c);
 57         Update(rz,mid+1,tr,c);
 58     }
 59     max_sum[u]=max(max_sum[2*u],max_sum[2*u+1]);
 60 }
 61 
 62 int find(__int64 tmp, int n)
 63 {
 64     int l=1, r=n, mid;
 65     while(l<=r)
 66     {
 67         mid=(l+r)>>1;
 68         if(X[mid]==tmp) return mid;
 69         else if(X[mid]<tmp) l=mid+1;
 70         else r=mid-1;
 71     }
 72 }
 73 
 74 int main()
 75 {
 76     int n, w, h;
 77     while(cin >> n >> w >> h)
 78     {
 79         memset(flag,0,sizeof(flag));
 80         memset(max_sum,0,sizeof(max_sum));
 81         int num=0, ep=1;
 82         __int64 x,y,c;
 83         for(int i=0; i<n; i++)
 84         {
 85             scanf("%I64d%I64d%I64d",&x,&y,&c);
 86             line[++num]=Node(x,x+w-1,y,c);
 87             X[num]=x;
 88             line[++num]=Node(x,x+w-1,y+h-1,-c);
 89             X[num]=x+w-1;
 90         }
 91         sort(X+1,X+num+1);
 92         sort(line+1,line+num+1);
 93         for(int i=2; i<=num; i++)
 94             if(X[i]!=X[ep])  X[++ep]=X[i];
 95         __int64 ans=0, lbd=1, rbd=ep;
 96         for(int i=1; i<num; i++)
 97         {
 98             int l=find(line[i].lx,ep);
 99             int r=find(line[i].rx,ep);
100             Update(1,lbd,rbd,l,r,line[i].s);
101             ans=max(ans,max_sum[1]);
102         }
103         cout << ans <<endl;
104     }
105     return 0;
106 }

 

     5、hdu3642  Get The Treasury

     題目大意:給你n個立方體,求相交區域大於等於三次的體積和。

     解題思路: 其實吧,三維的和二維的其實差不多。如果一個立方體的高為h,那么我們可以把它分割成h層,對每一層進行面積並的掃描,注意是從下往上。

     這題離散化x坐標是為了方便建樹,離散化z坐標是為了節約時間。 剩下的問題就變成了如何求覆蓋大於等於三次體積范圍。

     問題同樣可以轉化為二維的面積並求解,對每一層進行掃描,每層求覆蓋大於等於三次的面積區域。每層求的結果加起來就是答案了。

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define lz 2*u,l,mid
  8 #define rz 2*u+1,mid+1,r
  9 const int maxn=2222;
 10 typedef long long lld;
 11 int flag[4*maxn];
 12 lld sum1[4*maxn], sum2[4*maxn], sum3[4*maxn];
 13 int X[maxn], Z[maxn];
 14 
 15 struct Node
 16 {
 17     int lx, rx, y, z1, z2, s;
 18     Node() {}
 19     Node(int lx_, int rx_ , int y_, int zm_, int zl_, int s_)
 20     {
 21         lx=lx_, rx=rx_, y=y_, z1=zm_, z2=zl_, s=s_;
 22     }
 23     bool operator<(const Node &S) const
 24     {
 25         if(y==S.y) return s>S.s;
 26         else return y<S.y;
 27     }
 28 } line[maxn], tmp[maxn];
 29 
 30 void push_up(int u, int l, int r)
 31 {
 32     if(flag[u]>=3)   ///開始寫成了flag[u]==3, wrong answer了一個晚上
 33     {
 34         sum3[u]=sum2[u]=sum1[u]=X[r+1]-X[l];
 35     }
 36     else if(flag[u]==2)
 37     {
 38 
 39         sum2[u]=sum1[u]=X[r+1]-X[l];
 40         if(l==r)sum3[u]=0;
 41         else
 42             sum3[u]=sum1[2*u]+sum1[2*u+1];
 43     }
 44     else if(flag[u]==1)
 45     {
 46         sum1[u]=X[r+1]-X[l];
 47         if(l==r)sum2[u]=sum3[u]=0;
 48         else
 49         {
 50             sum2[u]=sum1[2*u]+sum1[2*u+1];
 51             sum3[u]=sum2[2*u]+sum2[2*u+1];
 52         }
 53     }
 54     else
 55     {
 56         if(l==r)sum1[u]=sum2[u]=sum3[u]=0;
 57         else
 58         {
 59             sum1[u]=sum1[2*u]+sum1[2*u+1];
 60             sum2[u]=sum2[2*u]+sum2[2*u+1];
 61             sum3[u]=sum3[2*u]+sum3[2*u+1];
 62         }
 63     }
 64 }
 65 
 66 void Update(int u, int l, int r, int tl, int tr, int c)
 67 {
 68     if(tl>tr) return ;
 69     if(tl<=l&&r<=tr)
 70     {
 71         flag[u]+=c;
 72         push_up(u,l,r);
 73         return ;
 74     }
 75     int mid=(l+r)>>1;
 76     if(tr<=mid) Update(lz,tl,tr,c);
 77     else if(tl>mid) Update(rz,tl,tr,c);
 78     else
 79     {
 80         Update(lz,tl,mid,c);
 81         Update(rz,mid+1,tr,c);
 82     }
 83     push_up(u,l,r);
 84 }
 85 
 86 int find(int tmp, int n)
 87 {
 88     int l=1, r=n, mid;
 89     while(l<=r)
 90     {
 91         mid=(l+r)>>1;
 92         if(X[mid]==tmp) return mid;
 93         else if(X[mid]<tmp) l=mid+1;
 94         else r=mid-1;
 95     }
 96 }
 97 
 98 int main()
 99 {
100     int T, n, tcase=0;
101     cin >> T;
102     while(T--)
103     {
104         cin >> n ;
105         int num=0;
106         for(int i=0; i<n; i++)
107         {
108             int x1, y1, x2, y2, z1, z2;
109             scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
110             line[++num]=Node(x1,x2,y1,z1,z2,1);
111             X[num]=x1, Z[num]=z1;
112             line[++num]=Node(x1,x2,y2,z1,z2,-1);
113             X[num]=x2, Z[num]=z2;
114         }
115         sort(line+1,line+num+1);
116         sort(X+1,X+num+1);
117         sort(Z+1,Z+num+1);
118         int ep=1, m=1;
119         for(int i=2; i<=num; i++)
120             if(X[i]!=X[ep]) X[++ep]=X[i];
121         for(int i=2; i<=num; i++)
122             if(Z[i]!=Z[m]) Z[++m]=Z[i];
123         lld ans=0;
124         for(int j=1; j<m; j++)
125         {
126             memset(flag,0,sizeof(flag));
127             memset(sum1,0,sizeof(sum1));
128             memset(sum2,0,sizeof(sum2));
129             memset(sum3,0,sizeof(sum3));
130             lld tp=0, cnt=0;
131             for(int i=1; i<=num; i++)
132             {
133                 if(line[i].z1<=Z[j]&&Z[j]<line[i].z2)
134                     tmp[++cnt]=line[i];
135             }
136             for(int i=1; i<cnt; i++)
137             {
138                 int l=find(tmp[i].lx,ep);
139                 int r=find(tmp[i].rx,ep)-1;
140                 Update(1,1,ep-1,l,r,tmp[i].s);
141                 tp+=(lld)sum3[1]*(lld)(tmp[i+1].y-tmp[i].y);
142             }
143             ans+=(lld)tp*(lld)(Z[j+1]-Z[j]);
144         }
145         printf("Case %d: %I64d\n",++tcase,ans);
146     }
147     return 0;
148 }

 

     6、hdu3255 Farming

      題目大意:有n塊蔬菜地,每塊蔬菜地中種一種作物,每種作物都有一個價格,當在同一區域內種植了兩種不同的作物時,作物價格大的生存下來,作物價格小的死亡。問你最后能得到的最大利潤是多少。

     解題思路: 每塊蔬菜地種植蔬菜收獲的利潤為 val=x*y*price。  面積乘以價格,題目的重點轉換在於如何確定重疊區域怎么讓它種植最貴的蔬菜。

     觀察利潤計算公式 :   x*y*price <==> x*y*h    可以轉換為求體積並。

     說實話,這題一看真的很簡單,一交就是一直錯,代碼每個部分都有調試N遍,重拍了幾次。 昨天一個就一直在調試,五個小時無果。和別人一對照,思路一點都沒錯,今天下午再重拍了一遍代碼,又開始了Wrong answer之路,然后帶着那顆受傷的心去改數組范圍。點最大3W, 我線段樹開12W多,存X的點開6W多。 點開4W錯, 6W TLE, 索性所有數組范圍開150005,錯。 150101 AC。  把我整傷心了!!!

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define lson l,mid,ID<<1
  8 #define rson mid+1,r,ID<<1|1
  9 const int maxn=150101;
 10 typedef long long lld;
 11 int flag[maxn];
 12 lld sum[maxn];
 13 int X[maxn], Z[maxn];
 14 
 15 struct Node   ///這題快把我整哭了
 16 {
 17     int lx, rx, y, z, s;
 18     Node(){}
 19     Node(int lx_, int rx_ , int y_, int z_, int s_)
 20     {
 21         lx=lx_, rx=rx_, y=y_, z=z_, s=s_;
 22     }
 23     bool operator<(const Node &S) const
 24     {
 25         if(y==S.y) return s>S.s;
 26         else return y<S.y;
 27     }
 28 }line[maxn], tmp[maxn];
 29 
 30 
 31 int find(int x, int M)
 32 {
 33     int l,r,m;
 34     l=1;
 35     r=M;
 36     while(l<=r)
 37     {
 38         m=(l+r)>>1;
 39         if(X[m]==x)
 40             return m;
 41         if(X[m]<x)
 42             l=m+1;
 43         else
 44             r=m-1;
 45     }
 46 }
 47 void Push_up(int ID,int l,int r)
 48 {
 49     if(flag[ID])sum[ID]=X[r+1]-X[l];
 50     else if(l==r)sum[ID]=0;
 51     else sum[ID]=sum[ID<<1]+sum[ID<<1|1];
 52 }
 53 void Update(int x,int y,int z,int l,int r,int ID)
 54 {
 55     int mid;
 56     if(x<=l&&r<=y)
 57     {
 58         flag[ID]+=z;
 59         Push_up(ID,l,r);
 60         return ;
 61     }
 62     mid=(l+r)>>1;
 63     if(x<=mid)
 64         Update(x,y,z,lson);
 65     if(y>mid)
 66         Update(x,y,z,rson);
 67     Push_up(ID,l,r);
 68 }
 69 
 70 int main()
 71 {
 72     int n, m, T, tcase=0;
 73     cin >> T;
 74     while(T--)
 75     {
 76         cin >> n >> m;
 77         Z[0]=0;
 78         for(int i=1; i<=m; i++)
 79             cin >> Z[i];
 80         int num=0;
 81         for(int i=0; i<n; i++)
 82         {
 83             int x1, x2, y1, y2, id;
 84             scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&id);
 85             line[++num]=Node(x1,x2,y1,Z[id],1);
 86             X[num]=x1;
 87             line[++num]=Node(x1,x2,y2,Z[id],-1);
 88             X[num]=x2;
 89         }
 90         sort(Z,Z+m+1);
 91         sort(X+1,X+num+1);
 92         sort(line+1,line+num+1);
 93         int ep=1;
 94         for(int i=2; i<=num; i++)
 95             if(X[i]!=X[ep]) X[++ep]=X[i];
 96         lld ans=0;
 97         for(int i=0; i<m; i++)
 98         {
 99             memset(sum,0,sizeof(sum));
100             memset(flag,0,sizeof(flag));
101             lld tp=0, cnt=0;
102             for(int j=1; j<=num; j++)
103                if(line[j].z>Z[i]) tmp[++cnt]=line[j];
104             for(int j=1; j<cnt; j++)
105             {
106                 int l=find(tmp[j].lx,ep);
107                 int r=find(tmp[j].rx,ep)-1;
108                 Update(l,r,tmp[j].s,1,ep-1,1);
109                 tp+=sum[1]*(lld)(tmp[j+1].y-tmp[j].y);
110             }
111             ans+=tp*(lld)(Z[i+1]-Z[i]);
112         }
113         printf("Case %d: %I64d\n",++tcase,ans);
114     }
115     return 0;
116 }

 

 

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM