[ybtoj 4.2.3 /UVA12983] The Battle of Chibi
作者:互联网
题意
在一个长度为 n n n 的序列中找到长度为 m m m 的严格上升子序列的个数 ,答案对 1 0 9 + 7 10^9+7 109+7 取模。 1 ⩽ n , m ⩽ 1 0 3 1\leqslant n,m \leqslant 10^3 1⩽n,m⩽103
思路
设
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示长度为
i
i
i,以
j
j
j 结尾的子序列的数量,可以列得方程
f
[
i
]
[
j
]
=
∑
k
<
j
f
[
i
−
1
]
[
k
]
f[i][j]=\sum_{k<j}{f[i-1][k]}
f[i][j]=k<j∑f[i−1][k]
离散化后直接做的话是
O
(
m
n
2
)
O(mn^2)
O(mn2) 的,不可接受。
考虑优化计算方式。
注意到这里的计算相当于统计
f
[
i
−
1
]
f[i-1]
f[i−1] 的前缀和,
题目就转化为要求实现快速求前缀和,可以用树状数组来维护。
复杂度就被降为
O
(
m
n
log
n
)
O(mn\log n)
O(mnlogn)
代码
#include<bits/stdc++.h>
#define N 1010
#define mod 1000000007
using namespace std;
int tree[N][N],a[N],b[N],n,m,tmp,tot,ans,T;
void add(int id,int x,int num){for(;x<=tot;x+=x&-x)tree[id][x]=(tree[id][x]+num)%mod;}
int ask(int id,int x){int cnt=0;for(;x;x-=x&-x)cnt=(cnt+tree[id][x])%mod;return cnt;}
int main()
{
cin>>T;
for(int u=1;u<=T;u++)
{
memset(tree,0,sizeof(tree));
ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);tot=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+tot+1,a[i])-b;//离散化
add(1,a[i],1);//初始化
for(int j=1;j<m;j++)
add(j+1,a[i],ask(j,a[i]-1));
}
ans=ask(m,tot);//答案相当于求f[m]的前缀和,可以直接询问
printf("Case #%d: %d\n",u,ans);
}
}
标签:cnt,int,ybtoj,tree,Chibi,add,序列,Battle,leqslant 来源: https://blog.csdn.net/weixin_45523071/article/details/117826015