2023“钉耙编程”中国大学生算法设计超级联赛(3)Out of Control
题目大意
有
n
n
n个数
x
1
,
x
2
,
…
,
x
n
x_1,x_2,\dots,x_n
x1,x2,…,xn,在其中选
k
k
k个数依次放入栈中。如果当前放入栈中的数
x
i
x_i
xi小于栈顶的数,则向栈中放入与先前的栈顶相同的数而不是
x
i
x_i
xi。
对于每一个
i
∈
[
1
,
n
]
i\in[1,n]
i∈[1,n],求
k
=
i
k=i
k=i时不同栈的数量。
有
T
T
T组数据。
1
≤
T
≤
100
,
1
≤
n
≤
3000
1\leq T\leq 100,1\leq n\leq 3000
1≤T≤100,1≤n≤3000
题解
将
x
i
x_i
xi从小到大排序,那么一组可能的栈
s
1
,
s
2
,
…
,
s
k
s_1,s_2,\dots,s_k
s1,s2,…,sk合法当且仅当
s
s
s中仅包含
x
x
x中的数,且对于所有的
1
≤
i
≤
k
1\leq i\leq k
1≤i≤k都要满足
s
i
≥
x
i
s_i\geq x_i
si≥xi。
我们先将
x
i
x_i
xi离散化一下。考虑
D
P
DP
DP。设
f
i
,
j
f_{i,j}
fi,j表示合法的长度为
i
i
i的满足
s
i
=
j
s_i=j
si=j的序列
s
s
s的数量,那么状态转移式为
f
i
,
j
=
∑
k
=
1
j
f
i
−
1
,
k
f_{i,j}=\sum\limits_{k=1}^jf_{i-1,k}
fi,j=k=1∑jfi−1,k
其中
j
≥
x
i
j\geq x_i
j≥xi。
可以用前缀和来优化
D
P
DP
DP。
时间复杂度为
O
(
∑
n
2
)
O(\sum n^2)
O(∑n2)。
code
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
int t,n,a[3005],num[3005];
long long sum,ans,f[3005][3005];
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
num[i]=a[i];
}
sort(num+1,num+n+1);
int vt=unique(num+1,num+n+1)-num-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(num+1,num+vt+1,a[i])-num;
}
sort(a+1,a+n+1);
for(int i=1;i<=vt;i++){
f[1][i]=1;
}
printf("%d\n",vt);
for(int i=2;i<=n;i++){
sum=ans=0;
for(int j=a[i-1];j<a[i];j++) sum=(sum+f[i-1][j])%mod;
for(int j=a[i];j<=vt;j++){
sum=(sum+f[i-1][j])%mod;
f[i][j]=sum;
ans=(ans+f[i][j])%mod;
}
printf("%lld\n",ans);
}
}
return 0;
}