HDU 5773 The All-purpose Zero
题目:The All-purpose Zero
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5773
题意:给一个大小为n的数组a[],包含0到100万的数字,0可以当作任意整数,要求输出最长单调递增子序列的长度。(子序列可以跳着选,子序列必须严格递增)
思路:
动态规划+二分+线段树。比赛时候想出来的解法就是这个了,结果看别人的解法简直简单粗暴,学到了。
动态规划+二分在nlogn时间里面找出普通的(也就是不包含0)的解,线段树处理含0的情况。
dp[i]表示长度为i 的子序列末尾最小是几。令maxt为当前最大的长度,初始为0,dp[0]=-100010,然后i 从1遍历到n,每次都从dp[0]到dp[maxt]里面找出序列长度最大的且末尾小于a[i]的位置pos(这里要用到二分,注意这里的dp数组肯定是有序的),如果pos+1大于maxt,更新maxt,如果没有则判断dp[pos+1]和a[i]的大小,如果a[i]小就更新dp[pos+1]。
遇到0的时候,因为0可以当作任意数,所以每个dp[i]后面都可以接个比dp[i]大1的数,因为所有子序列长度+1,所以我们不移动dp[i],而是给另外的一个变量cha++,最终cha+maxt就是答案。有了cha,我们还是要给所有的dp[i]+1,这里可以用到线段树,区间更新,单点取值。
AC代码:
1 #include<stdio.h> 2 int min(int a,int b) 3 { 4 return a<b?a:b; 5 } 6 struct Node 7 { 8 int w; 9 int add; 10 int l,r; 11 int mid() 12 { 13 return (l+r)/2; 14 } 15 }; 16 Node v[400040]; 17 void build(int l,int r,int rt) 18 { 19 v[rt].w=0; 20 v[rt].add=0; 21 v[rt].l=l; 22 v[rt].r=r; 23 if(l==r) return ; 24 build(l,v[rt].mid(),rt<<1); 25 build(v[rt].mid()+1,r,rt<<1|1); 26 } 27 void fu(int pos,int val,int rt) 28 { 29 if(v[rt].l==v[rt].r) 30 { 31 v[rt].w=val; 32 v[rt].add=0; 33 return ; 34 } 35 if(v[rt].add>0) 36 { 37 v[rt<<1].add+=v[rt].add; 38 v[rt<<1|1].add+=v[rt].add; 39 v[rt].add=0; 40 } 41 int mid=v[rt].mid(); 42 if(pos<=mid) return fu(pos,val,rt<<1); 43 else return fu(pos,val,rt<<1|1); 44 } 45 void update(int l,int r,int rt) 46 { 47 if(v[rt].l==l&&v[rt].r==r) 48 { 49 v[rt].add++; 50 return ; 51 } 52 int mid=v[rt].mid(); 53 if(l>mid) 54 update(l,r,rt<<1|1); 55 else if(r<=mid) update(l,r,rt<<1); 56 else 57 { 58 update(l,mid,rt<<1); 59 update(mid+1,r,rt<<1|1); 60 } 61 } 62 int look(int pos,int rt) 63 { 64 if(v[rt].l==v[rt].r) 65 { 66 v[rt].w+=v[rt].add; 67 v[rt].add=0; 68 return v[rt].w; 69 } 70 if(v[rt].add>0) 71 { 72 v[rt<<1].add+=v[rt].add; 73 v[rt<<1|1].add+=v[rt].add; 74 v[rt].add=0; 75 } 76 int mid=v[rt].mid(); 77 if(pos<=mid) return look(pos,rt<<1); 78 else return look(pos,rt<<1|1); 79 } 80 int er(int l,int r,int x) 81 { 82 while(l<r) 83 { 84 int mid=(l+r+1)>>1; 85 if(look(mid,1)>=x) r=mid-1; 86 else l=mid; 87 } 88 return l; 89 } 90 int a[100010]; 91 int main() 92 { 93 int t,n,cas=1; 94 scanf("%d",&t); 95 while(t--) 96 { 97 scanf("%d",&n); 98 for(int i=1;i<=n;i++) 99 { 100 scanf("%d",a+i); 101 } 102 build(0,n,1); 103 int maxt=0,cha=0; 104 fu(0,-100010,1); 105 for(int i=1;i<=n;i++) 106 { 107 if(a[i]==0) 108 { 109 cha++; 110 update(0,i,1); 111 continue; 112 } 113 int pos=er(0,maxt,a[i]); 114 if(pos==maxt) 115 { 116 maxt++; 117 fu(maxt,a[i],1); 118 } 119 else 120 { 121 fu(pos+1,min(look(pos+1,1),a[i]),1); 122 } 123 } 124 printf("Case #%d: %d\n",cas++,maxt+cha); 125 } 126 return 0; 127 }