dp+树状数组
给定一个长度为 N 的序列 A,求 A有多少个长度为 M 的严格递增子序列。
输入格式
第一行包含整数 TT,表示共有 T 组测试数据。
每组数据,第一行包含两个整数 N 和 M。
第二行包含 N 个整数,表示完整的序列 A。
输出格式
每组数据输出一个结果,每个结果占一行。
输出格式为 Case #x: y
,x 为数据组别序号,从 11 开始,yy 为结果。
由于答案可能很大,请你输出对 1e9+7取模后的结果。
数据范围
1≤T≤100
1≤M≤N≤1000
∑Ti=1Ni×Mi≤1e7
序列中的整数的绝对值不超过1e9。
输入样例:
2
3 2
1 2 3
3 2
3 2 1
输出样例:
Case #1: 3
Case #2: 0
f[maxn][maxn];//以第i个结尾时,有j个递增的方案数
优化之前的代码时这样的:
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=1e3+100; const int mod=1e9+7; int a[maxn],t; int f[maxn][maxn];//以第i个结尾时,有j个递增的方案书 int n,m; int main(){ cin>>t; int kase=0; while(t--){ memset(f,0,sizeof(f)); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } f[1][1]=1; for(int i=1;i<=n;i++){ f[i][1]=1; } for(int i=2;i<=n;i++){ for(int j=1;j<m;j++){ for(int k=1;k<i;k++){ if(a[i]>a[k]){ f[i][j+1]+=f[k][j]; } f[i][j+1]%=mod; } } } int ans=0; for(int i=1;i<=n;i++){ //cout<<f[i][m]<<endl; ans+=f[i][m]; ans%=mod; } printf("Case #%d: %d\n",++kase,ans); } }
然后可以用线段树或者树状数组优化掉一维
#include<iostream> #include<algorithm> #include<cstring> typedef long long ll; using namespace std; const int maxn=1e3+100; const int mod=1e9+7; int a[maxn],t; int f[maxn][maxn];//以第i个结尾时,有j个递增的方案数 int n,m; int cnt=0; int c[maxn]; int num[maxn]; int lowbit(int x){ return x&-x; } void add(int x,int k){ for(int i=x;i<=cnt;i+=lowbit(i)){ c[i]=(c[i]+k)%mod; } } int getsum(int x){ int ans=0; for(int i=x;i>0;i-=lowbit(i)){ ans=(ans+c[i])%mod; } return ans; } int main(){ cin>>t; int kase=0; while(t--){ memset(f,0,sizeof(f)); cin>>n>>m; cnt=0; for(int i=1;i<=n;i++){ cin>>a[i]; num[++cnt]=a[i]; } sort(num+1,num+cnt+1); cnt=unique(num+1,num+cnt+1)-(num+1); for(int i=1;i<=n;i++){ a[i]=lower_bound(num+1,num+cnt+1,a[i])-num; } for(int i=1;i<=n;i++){ f[i][1]=1; } for(int j=2;j<=m;j++){ for(int i=1;i<=cnt;i++){ c[i]=0; } for(int i=1;i<=n;i++){ f[i][j]=getsum(a[i]-1); add(a[i],f[i][j-1]); } } int ans=0; for(int i=1;i<=n;i++){ ans+=f[i][m]; ans%=mod; } printf("Case #%d: %d\n", ++kase,ans); } }