【题意】给出N(<=1000)长的序列,求出M长度的上升子序列的数量。
【分析】首先,容易想到N^3的DP,dp[i][j]表示以a[i]结尾长度为j的子序列的数量。
那么转移方程为: dp[i][j] = sum{dp[k][j-1]} (1 <= k < i && a[i] > a[k]),初始话长度为1时dp值为1。
但是N^3必然超时,所以想到优化,观察sum{dp[k][j-1]}其实就是对于每个i之前所有值小于a[i]时候的总和,这个可以用树状数组维护,那么再给每个长度建一颗树状数组即可,这样复杂度O(N^2 * log(N))。
【N^3暴力DP超时代码】
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 1000+10, MOD = 1000000007;
int dp[MAXN][MAXN],a[MAXN];
int main()
{
#ifdef SHY
freopen("d:\\1.txt", "r", stdin);
#endif
int t,count = 0;
scanf("%d", &t);
while(t--)
{
int n,m;
scanf("%d%d", &n,&m);
repe(i,1,n) scanf("%d", &a[i]);
a[n+1] = 0;
clc(dp,0);
repe(i,1,n)
{
repe(j,1,m)
{
if(j == 1) dp[i][j] = 1;
else
{
rep(k,0,i)
{
if(a[k] < a[i])
dp[i][j] = (dp[i][j]+dp[k][j-1])%MOD;
}
}
}
}
int ans = 0;
repe(i,1,n) ans = (ans+dp[i][m])%MOD;
printf("Case #%d: %d\n", ++count,ans);
}
return 0;
}
【AC CODE】2698ms
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 1000+10, MOD = 1000000007;
int dp[MAXN][MAXN],a[MAXN],sot[MAXN],mx;
struct Tree{
int cnt[MAXN];
void init()
{
clc(cnt,0);
}
int lowbit(int x){return x&-x;}
void add(int x, int v)
{
while(x <= mx)
{
cnt[x] = (cnt[x]+v)%MOD;
x += lowbit(x);
}
}
int query(int x)
{
int ans = 0;
while(x > 0)
{
ans = (ans+cnt[x])%MOD;
x -= lowbit(x);
}
return ans;
}
}tree[MAXN];
int main()
{
#ifdef SHY
freopen("d:\\1.txt", "r", stdin);
#endif
int t,count = 0;
scanf("%d", &t);
while(t--)
{
int n,m;
scanf("%d%d", &n,&m);
repe(i,1,n) scanf("%d", &a[i]),sot[i] = a[i];
sort(sot+1,sot+1+n);
mx = unique(sot+1,sot+1+n)-sot-1;
repe(i,1,n) a[i] = lower_bound(sot+1,sot+1+mx,a[i])-sot,tree[i].init();
clc(dp,0);
repe(i,1,n)
{
repe(j,1,m)
{
if(j == 1) dp[i][j] = 1;
else dp[i][j] = tree[j-1].query(a[i]-1);
tree[j].add(a[i],dp[i][j]);
}
}
int ans = 0;
repe(i,1,n) ans = (ans+dp[i][m])%MOD;
printf("Case #%d: %d\n", ++count,ans);
}
return 0;
}