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 }

 

posted @ 2016-07-28 18:13  hchlqlz  阅读(303)  评论(0编辑  收藏  举报