您的位置:首页 > 其它

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

2013-04-17 00:03 357 查看
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

//hdu3450
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100005,MOD = 9901;

int a
,b
,hash
,c
,tot;

int lowbit(int x)    {return x&(-x);}

void add(int id,int x)
{
for(int i=id;i<=tot;i+=lowbit(i))
c[i]= (c[i]+x)%MOD;
}

int query(int id)
{
int sum=0;
for(int i=id;i>=1;i-=lowbit(i))
sum = (sum+c[i])%MOD;
return sum;
}

int findR(int v)
{
int i=1,j=tot+1,mid;

while( i<j )
{
mid=(i+j)/2;
if( hash[mid]<=v )    i=mid+1;
else j=mid;
}
return i-1;
}

int findL(int v)
{
int i=1,j=tot+1,mid;

while( i<j )
{
mid=(i+j)/2;
if( hash[mid]<v )    i=mid+1;
else j=mid;
}
return i-1;
}

int main()
{
int n,d;

while( ~scanf("%d%d",&n,&d) )
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
tot=1;
hash[1]=b[1];
for(int i=2;i<=n;i++)
if( b[i]!=b[i-1] )
hash[++tot]=b[i];
memset(c+1,0,tot*sizeof(c[0]));
for(int i=1;i<=n;i++)
{
int L=findL(a[i]-d),id=findL(a[i])+1,R=findR(a[i]+d);
int tmp=query(R)-query(L);
add(id,tmp+1);
}
printf("%d\n",((query(tot)-n)%MOD+MOD)%MOD);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: