POJ1769 Minimizing maximizer(DP + 线段树)
题目大概就是要,给一个由若干区间[Si,Ti]组成的序列,求最小长度的子序列,使这个子序列覆盖1到n这n个点。
- dp[i]表示从第0个到第i个区间且使用第i个区间,覆盖1到Ti所需的最少长度
- 对于Si=1的i区间dp值就是1了,要求的答案就是所有Ti=n的最小的dp值
转移就是,dp[i]=dp[j]+1,Si<=Tj<=Ti。不过枚举转移这样显然会T的,可以转化成RMQ来提升效率,用线段树成段更新成段查询即可,即维护1...n的覆盖所需区间的最小值,这样时间复杂度O(mlogn)。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 #define MAXN 55555 5 6 inline int min(int a,int b){ 7 if(a==0) return b; 8 if(b==0) return a; 9 if(a<b) return a; 10 return b; 11 } 12 13 int tree[MAXN<<2],tag[MAXN<<2],N,x,y,z; 14 void update(int i,int j,int k){ 15 if(x<=i && j<=y){ 16 tree[k]=min(tree[k],z); 17 tag[k]=min(tag[k],z); 18 return; 19 } 20 if(tag[k]){ 21 tree[k<<1]=min(tree[k<<1],tag[k]); 22 tree[k<<1|1]=min(tree[k<<1|1],tag[k]); 23 tag[k<<1]=min(tag[k<<1],tag[k]); 24 tag[k<<1|1]=min(tag[k<<1|1],tag[k]); 25 tag[k]=0; 26 } 27 int mid=i+j>>1; 28 if(x<=mid) update(i,mid,k<<1); 29 if(y>mid) update(mid+1,j,k<<1|1); 30 tree[k]=min(tree[k<<1],tree[k<<1|1]); 31 } 32 int query(int i,int j,int k){ 33 if(x<=i && j<=y){ 34 return tree[k]; 35 } 36 if(tag[k]){ 37 tree[k<<1]=min(tree[k<<1],tag[k]); 38 tree[k<<1|1]=min(tree[k<<1|1],tag[k]); 39 tag[k<<1]=min(tag[k<<1],tag[k]); 40 tag[k<<1|1]=min(tag[k<<1|1],tag[k]); 41 tag[k]=0; 42 } 43 int mid=i+j>>1,res=0; 44 if(x<=mid) res=min(res,query(i,mid,k<<1)); 45 if(y>mid) res=min(res,query(mid+1,j,k<<1|1)); 46 return res; 47 } 48 49 int main(){ 50 int n,m,a,b; 51 scanf("%d%d",&n,&m); 52 for(N=1; N<n; N<<=1); 53 int ans=0; 54 for(int i=0; i<m; ++i){ 55 scanf("%d%d",&a,&b); 56 int res=0; 57 if(a==1) res=1; 58 else{ 59 x=a; y=b; 60 res=query(1,N,1); 61 if(res) ++res; 62 } 63 x=1; y=b; z=res; 64 update(1,N,1); 65 if(b==n) ans=min(ans,res); 66 } 67 printf("%d\n",ans); 68 return 0; 69 }