赤壁之战
Posted jjl0229
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了赤壁之战相关的知识,希望对你有一定的参考价值。
题目连接:
https://www.acwing.com/problem/content/299/
Description
给定一个长度为N的序列A,求A有多少个长度为M的严格递增子序列。
Input
第一行包含整数T,表示共有T组测试数据。
每组数据,第一行包含两个整数N和M。
第二行包含N个整数,表示完整的序列A。
Output
每组数据输出一个结果,每个结果占一行。
输出格式为“Case #x: y”,x为数据组别序号,从1开始,y为结果。
由于数据可能很大,请你输入对(10^9+7)取模后的结果。
Sample Input
2
3 2
1 2 3
3 2
3 2 1
Sample Output
Case #1: 3
Case #2: 0
Hint
数据范围
(1≤T≤100,
1≤M≤N≤1000,
N×M的和小于10^7)
序列中的整数的绝对值不超过(10^9)。
题意
在长度为n的序列中求长度为m的严格递增子序列的个数
题解:
首先考虑二维DP,f[i][j]表示前i个且第i个作为最后一个数的长度为j的严格递增子序列个数,f[i][j]+=f[k][j-1](k<i & a[k]<a[i]) DP可以写成
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=1;k<i;++k)
f[i][j]+=f[k][j-1]
复杂度O(1e10)了,我们需要降掉一维,或者变成n变logn的复杂度。
第三层循环我们需要找到k<i且a[k]<a[i]的所有f[k][j]的和,即在a[i]之前的和,这样很容易想到用树状数组点插入的做法了,在算第j维时,求j-1维在树状数组中a[i]点之前的前缀和,可以维护一个m维的树状数组。实际上,我们每次只需要j的上一维的树状数组,把第一第二层循环交换一下,就只用维护一个一维的树状数组,每次用完清空,也只用一维的f[i]记录上一维的f[i][j-1]。先更新j-1维的f[i]更新到a[i],再用f[i]的存a[i]-1之前的f[k][j-1]的和即得f[i][j]。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1010,mod=1e9+7;
int f[N],tr[N],a[N],b[N];
inline int lowbit(int x) {
return x&(-x);
}
void add(int x,int y){
while(x<N){
(tr[x]+=y)%=mod;
x+=lowbit(x);
}
}
int sum(int x){
int res=0;
while(x>0){
(res+=tr[x])%=mod;
x-=lowbit(x);
}
return res;
}
int main(){
int T;
cin>>T;
for(int cas=1;cas<=T;++cas){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+1+n);
int tot=unique(b+1,b+1+n)-b;
for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
for(int i=1;i<=n;++i) f[i]=1;
for(int i=2;i<=m;++i){
for(int j=1;j<=tot;++j) tr[j]=0;
for(int j=1;j<=n;++j){
add(a[j],f[j]);
f[j]=sum(a[j]-1);
}
}
int res=0;
for(int i=1;i<=n;++i) (res+=f[i])%=mod;
cout<<"Case #"<<cas<<": "<<res<<endl;
}
return 0;
}
以上是关于赤壁之战的主要内容,如果未能解决你的问题,请参考以下文章