HDU 4343 Interval query 倍增思想, DP

HDU 4343 Interval query 倍增思想, DP


注意当两个区间只有一个端点重合时,也算是合法的

题目大意:给定N(N<=100000)个区间(左闭右开)和M(M<=100000)个询问[l, r],问所有满足[s,t)包含于于[l, r]的区间中最多能选出多少个,使得他们两两不相交。
解题思路:首先将坐标离散化,将区间排序后删掉可以覆盖其他区间的大区间。
这时若将剩余区间的左端点坐标排序,左端点坐标必然严格上升且对应的右端点坐标也是严格上升的。
此题的贪心思想较为普及,即按y的升序进行贪心固定区间询问的最大数量,不再赘述。
设g[1][x]为从坐标x开始向右遇到的第一个有效区间的右端点坐标,另g[i][x] = g[1][g[i-1][x]],若不存在则为正无穷。
则g[k][x]表示从坐标x开始,在经过不相交的k个区间后,第k个区间的右端点的坐标。
对g进行倍增,即设f[k][x] = g[2^k][x]。则对于一次询问(l,r),可利用f枚举答案各个二进制数位得到答案。
f[k][x] = f[k-1][f[k-1][x]],f[0][x] = g[1][x]。g只存在于思维过程中。

 

 1 #include <bits/stdc++.h>
 2 using namespace std ; 
 3 
 4 const int N = 100051 ; 
 5 int n,m,opt[21][N*2],x[N*2] ; 
 6 int cnt ;  
 7 struct node  
 8 {  
 9     int l,r;  
10     void read(){ scanf("%d%d",&l,&r); x[cnt++]=l; x[cnt++]=r; }  
11     void print(){ printf("%d %d\n",l,r); }  
12     bool operator <(const node tmp) const{ return l<tmp.l||(l==tmp.l&&r<tmp.r); }      //
13 }no[N];  
14 
15 inline int getid(int val,bool f) 
16 //  对于每个  getid  l 求出的是 大于等于 l 的 x中最大数   
17 //  对于每个         r 求出的是 小于等于 r 的 x中最小数 
18 //  以此来确定  询问区间在  x 中的离散后的范围    
19 {
20     if(!f) return lower_bound(x,x+cnt,val) - x ;  
21     return upper_bound(x,x+cnt,val) - x - 1 ;  
22 }
23 
24 inline int calc(int l,int r) 
25 {
26     if( l > r ) return  0 ; 
27     if(l==cnt||r==-1) return 0 ; 
28     int ret = 0 ; 
29     for(int i=20;i>=0;i--)         /////
30         if(opt[i][l] <=r) {
31             ret+=(1<<i) ; 
32             l = opt[ i ][ l ] ; 
33         }
34     return ret ; 
35 }
36 
37 //在算的过程 会将 自己的区间给包含进去 
38 int main() 
39 {
40     while(~scanf("%d%d",&n,&m))  
41     {
42         cnt = 0 ; 
43         for(int i=0;i<n;i++) 
44             no[ i ].read() ; 
45         sort(no,no+n) ; 
46         sort(x,x+cnt) ; 
47         cnt = unique(x,x+cnt) - x ; 
48         for(int i=0;i<n;i++) 
49         {
50             no[ i ].l = getid(no[ i ].l,0) ; 
51             no[ i ].r = getid(no[ i ].r,1) ; 
52         }
53         x[ cnt ] = cnt ; 
54         int mn = cnt , r = n ; 
55         for(int i=cnt-1;i>=0;i--) 
56         {
57             while(r>0&&no[r-1].l>=i) 
58             {
59                 r-- ; 
60                 mn = min(mn,no[ r ].r) ;  
61             }
62             opt[0][i] = mn ;   //  opt[ 0 ][ i ] 表示坐标 i 向右  第一个不冲突 的 区间的右端点的坐标
63         }
64         for(int i=0;i<=20;i++) opt[ i ][cnt] = cnt ; 
65         for(int i=1;i<=20;i++)  
66             for(int j=0;j<cnt;j++) 
67                 opt[ i ][ j ] = opt[ i-1 ][ opt[i-1][j] ] ; 
68         while(m--) 
69         {
70             int l,r ; 
71             scanf("%d%d",&l,&r) ; 
72             l = getid(l,0) ; 
73             r = getid(r,1) ;  
74             printf("%d\n",calc(l,r)) ; 
75         }
76     }
77     return 0 ; 
78 }

 

posted @ 2017-07-07 14:29  third2333  阅读(164)  评论(0编辑  收藏  举报