hdu2227 && hdu3450 【树状数组优化dp】

hdu2227

树状数组的一个经典应用。

容易得到dp方程,dp[pos]表示前pos项里以a[pos]结尾的非降子序列的个数。

则有dp[pos]=sum( dp[i] , 1<= i <pos && a[i]<=a[pos] ) + 1。

复杂度为O(n^2),Time Limit Exceeded!!!

观察到a[i]<=a[pos] , 我们对原序列a从小到大排序,得到新的序列d,对d进行dp。

此时dp[pos]=sum(dp[i],1<=i<pos && d[i]的原序在d[pos]的原序之前 ) + 1。

对于元素d[pos] , d[1],...,d[pos-1]都<=d[pos],但是这些元素的原序可能在d[i]之后,而dp[i]只能依赖原序在d[i]之前的值来更新。

怎么解决这个问题呢?

我们对原序列a中的元素依次访问 , 记为a[i] , 并对应至d中相应的元素d[k](?) , 更新dp[k]=sum( dp[j] , 1<= j <k ) + 1。

为什么不用考虑顺序的问题了?因为此时只有原序在d[k]之前的元素被更新过。

而求和操作可以利用树状数组来实现。

(?)这里可以发现,a[i]到d[k]的对应是有问题的,这里用到了离散化( 排序,去重,二分对应标号 )(ps:其实离散化这个概念我刚接触,也还不太懂。。。希望懂的人不吝赐教)

//另外,当a[i]范围比较小时,可以不用离散化,直接以a[i]的值作为对应标号即可。

这样,这个问题在O(nlogn)的复杂度下Accepted了。

View Code
 1 //hdu2227
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N = 100005,MOD = 1000000007;
 7 typedef unsigned int LL;
 8 LL a[N],d[N],hash[N];
 9 int n,tot,c[N];
10 
11 int lowbit(int x)    {return x&(-x);}
12 
13 void add(int id,int x)
14 {
15     for(int i=id;i<=tot;i+=lowbit(i))
16         c[i] = (c[i]+x)%MOD;
17 }
18 
19 int query(int id)
20 {
21     int sum=0;
22     for(int i=id;i>=1;i-=lowbit(i))
23         sum=(sum+c[i])%MOD;
24     return sum;
25 }
26 
27 int binsearch(LL x)
28 {
29     int i=1,j=tot,mid;
30     
31     while( i<=j )
32     {
33         mid=(i+j)/2;
34         if( hash[mid]==x )    return mid;
35         if( hash[mid]<x )    i=mid+1;
36         else j=mid-1;
37     }
38 }
39 
40 int main()
41 {
42     while( ~scanf("%d",&n) )
43     {
44         for(int i=1;i<=n;i++)
45         {
46             scanf("%u",&a[i]);
47             d[i]=a[i];
48         }
49         sort(d+1,d+n+1);
50         tot=1;
51         hash[1]=d[1];
52         for(int i=2;i<=n;i++)
53             if( d[i]!=d[i-1] )
54                 hash[++tot]=d[i];
55         memset(c+1,0,tot*sizeof(c[0]));
56         for(int i=1;i<=n;i++)
57         {
58             int id=binsearch(a[i]);
59             int tmp=query(id);
60             add(id,tmp+1);
61         }
62         printf("%d\n",query(tot));
63     }
64     return 0;
65 }

hdu3450和hdu2227类似,就不解释了。

View Code
 1 //hdu3450
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N = 100005,MOD = 9901;
 7 
 8 int a[N],b[N],hash[N],c[N],tot;
 9 
10 int lowbit(int x)    {return x&(-x);}
11 
12 void add(int id,int x)
13 {
14     for(int i=id;i<=tot;i+=lowbit(i))
15         c[i]= (c[i]+x)%MOD;
16 }
17 
18 int query(int id)
19 {
20     int sum=0;
21     for(int i=id;i>=1;i-=lowbit(i))
22         sum = (sum+c[i])%MOD;
23     return sum;
24 }
25 
26 int findR(int v)
27 {
28     int i=1,j=tot+1,mid;
29 
30     while( i<j )
31     {
32         mid=(i+j)/2;
33         if( hash[mid]<=v )    i=mid+1;
34         else j=mid;
35     }
36     return i-1;
37 }
38 
39 int findL(int v)
40 {
41     int i=1,j=tot+1,mid;
42 
43     while( i<j )
44     {
45         mid=(i+j)/2;
46         if( hash[mid]<v )    i=mid+1;
47         else j=mid;
48     }
49     return i-1;
50 }
51 
52 int main()
53 {
54     int n,d;
55 
56     while( ~scanf("%d%d",&n,&d) )
57     {
58         for(int i=1;i<=n;i++)
59         {
60             scanf("%d",&a[i]);
61             b[i]=a[i];
62         }
63         sort(b+1,b+n+1);
64         tot=1;
65         hash[1]=b[1];
66         for(int i=2;i<=n;i++)
67             if( b[i]!=b[i-1] )
68                 hash[++tot]=b[i];
69         memset(c+1,0,tot*sizeof(c[0]));
70         for(int i=1;i<=n;i++)
71         {
72             int L=findL(a[i]-d),id=findL(a[i])+1,R=findR(a[i]+d);
73             int tmp=query(R)-query(L);
74             add(id,tmp+1);
75         }
76         printf("%d\n",((query(tot)-n)%MOD+MOD)%MOD);
77     }
78     return 0;
79 }

 

posted @ 2013-04-17 00:03  kiwi_bird  阅读(280)  评论(0编辑  收藏  举报