数据结构优化DP
考虑如下 DP 方程:
f
(
x
)
=
min
1
≤
i
<
x
且
h
(
i
)
<
h
(
x
)
{
f
(
i
)
+
w
(
i
)
}
f(x)=\min_{1\le i<x 且 h(i)<h(x)}\{f(i)+w(i)\}
f(x)=1≤i<x且h(i)<h(x)min{f(i)+w(i)}
考虑将
h
h
h 离散化,则能转移给
f
(
x
)
f(x)
f(x) 的
i
i
i 在 h 的离散序列中构成一段区间。可以用数据结构维护
h
h
h 的离散后的序列(通常需要支持插入、查询),时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
单纯地说太抽象了,还是来道例题看看吧。
例题:递增子序列
题目大意
各处一行 n n n 个数字,问长度为 m m m 的严格上升子序列有多少个。
思路
dp[i][j] 为以第 i 个元素结尾,长为 j 的上升子序列的个数,则有:dp[i][j]=sum(dp[k][j-1]),a[k]<a[i] && k<i
可以看出这个方程相当于一个区间求和。优化以树状数组为例:
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define _inf 0x3f3f3f3f
#define mod 123456789
#define ll long long
using namespace std;
int a[10010],b[10010],n,m;
ll c[105][10010];
int lowbit(int _)
{
return _&(-_);
}
void upd(int _,int d,int k)
{
while(_<=10000)
{
c[k][_]=(c[k][_]+d)%mod;
_+=lowbit(_);
}
}
ll get(int _,int k)
{
ll ret=0;
while(_)
{
ret=(ret+c[k][_])%mod;
_-=lowbit(_);
}
return ret;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int size=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+size+1,a[i])-b;//离散化
ll _;
for(int i=1;i<=n;i++)
{
upd(a[i],1,1);//初始化
for(int j=1;j<m;j++)
{
_=get(a[i]-1,j);//区间和
if(!_)break;
upd(a[i],_,j+1);
}
}
printf("%lld\n",get(10000,m)%mod);
}
return 0;
}