AT_dp_t Permutation
题意
有一个长为
N
N
N 的正整数排列。给定一个由 <
和 >
组成长为
N
−
1
N-1
N−1 的的字符串。
对于任意满足
1
≤
i
≤
N
−
1
1 \le i \le N-1
1≤i≤N−1 的字符
s
i
s_i
si,如果
s
i
s_i
si 是 <
则
P
i
<
P
i
+
1
P_i<P_{i+1}
Pi<Pi+1、如果
s
i
s_i
si 是 >
则
P
i
>
P
i
+
1
P_i>P_{i+1}
Pi>Pi+1。求满足这样的性质的排列
P
P
P 的方案数。
性质
我们发现,对于一个固定的 s s s,比如 ∣ s ∣ = 6 |s|=6 ∣s∣=6,于是我们可以发现:
P i P_i Pi 只能是 [ 1 , 2 , 3 , 4 , 5 , 6 ] [1,2,3,4,5,6] [1,2,3,4,5,6] 和 P i P_i Pi 只能是 [ 7 , 8 , 1 , 2 , 3 , 6 ] [7,8,1,2,3,6] [7,8,1,2,3,6] 它们算出来的答案是相等的。
证明显然,因为我们只需要考虑大小关系。
dp
考虑要填第 i i i 个数,直接暴力枚举它是啥。
令 f [ i ] [ j ] f[i][j] f[i][j] 表示 前 i i i 个数中最后一个数是 第 j j j 小 的方案数。
为什么是第 j j j 小?
根据性质可证,因为我们只考虑大小关系。
显然可得转移方程:
若
P
i
−
1
<
P
i
P_{i-1}<P_i
Pi−1<Pi:
则:
f
[
i
]
[
j
]
=
∑
k
=
1
j
−
1
f
[
i
−
1
]
[
k
]
f[i][j]=\sum_{k=1}^{j-1}f[i-1][k]
f[i][j]=∑k=1j−1f[i−1][k]
否则:
f
[
i
]
[
j
]
=
∑
k
=
j
+
1
i
f
[
i
−
1
]
[
k
]
f[i][j]=\sum_{k=j+1}^if[i-1][k]
f[i][j]=∑k=j+1if[i−1][k]
然后你就发现样例都过不了。
重新审视转移方程。
我们发现:
若
P
i
−
1
<
P
i
P_{i-1}<P_i
Pi−1<Pi:那
P
[
i
−
1
]
P[i-1]
P[i−1] 只能取比
P
[
i
]
P[i]
P[i] 小的,共
j
−
1
j-1
j−1 个。
但是反过来,你却发现一共有
i
−
j
+
1
i-j+1
i−j+1 个。
so:
f [ i ] [ j ] = { p i − 1 < p i ∑ k = 1 j − 1 f [ i − 1 ] [ j ] p i − 1 > p i ∑ k = j i f [ i − 1 ] [ j ] f[i][j]=\begin{cases} p_{i-1}<p_i &\sum_{k=1}^{j-1}f[i-1][j]\\ p_{i-1}>p_i &\sum_{k=j}^{i}f[i-1][j] \end{cases} f[i][j]={pi−1<pipi−1>pi∑k=1j−1f[i−1][j]∑k=jif[i−1][j]
前后缀和优化即可。
UVA1650 数字串 Number String
我们发现如果关系不确定那直接两种合并一起即可。
f [ i ] [ j ] f[i][j] f[i][j] 定义一样:
f [ i ] [ j ] = { p i − 1 < p i ∑ k = 1 j − 1 f [ i − 1 ] [ j ] p i − 1 > p i ∑ k = j i f [ i − 1 ] [ j ] p i − 1 ≠ p i ∑ k = 1 j − 1 f [ i − 1 ] [ j ] + ∑ k = j i f [ i − 1 ] [ j ] f[i][j]=\begin{cases} p_{i-1}<p_i &\sum_{k=1}^{j-1}f[i-1][j]\\ p_{i-1}>p_i &\sum_{k=j}^{i}f[i-1][j] \\ p_{i-1}\ne p_i &\sum_{k=1}^{j-1}f[i-1][j]+\sum_{k=j}^{i}f[i-1][j] \end{cases} f[i][j]=⎩ ⎨ ⎧pi−1<pipi−1>pipi−1=pi∑k=1j−1f[i−1][j]∑k=jif[i−1][j]∑k=1j−1f[i−1][j]+∑k=jif[i−1][j]
注意多测。
AT_abc209_f [ABC209F] Deforestation
n
n
n 个数:
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an。
每次可以选择一个
i
i
i,选择的代价是
a
i
−
1
+
a
i
+
a
i
+
1
a_{i-1}+a_i+a_{i+1}
ai−1+ai+ai+1,然后令
a
i
=
0
a_i=0
ai=0。
求有多少种方案,使得
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an 都变为
0
0
0 的总代价最小。特别的,
a
0
=
a
n
+
1
=
0
a_0=a_{n+1}=0
a0=an+1=0。
考虑
i
i
i 和
i
+
1
i+1
i+1 两个数,什么时候先选
i
i
i:
先 i i i: w 1 = a [ i − 1 ] + a [ i ] + a [ i + 1 ] + a [ i + 1 ] + a [ i + 2 ] w_1=a[i-1]+a[i]+a[i+1]+a[i+1]+a[i+2] w1=a[i−1]+a[i]+a[i+1]+a[i+1]+a[i+2]
先 i + 1 i+1 i+1: w 2 = a [ i ] + a [ i + 1 ] + a [ i + 2 ] + a [ i − 1 ] + a [ i ] w_2=a[i]+a[i+1]+a[i+2]+a[i-1]+a[i] w2=a[i]+a[i+1]+a[i+2]+a[i−1]+a[i]
w 1 < w 2 → a [ i + 1 ] < a [ i ] w_1<w_2 \rightarrow a[i+1]<a[i] w1<w2→a[i+1]<a[i]
so?
我们不需要考虑具体顺序,只需要先后,那就跟第一题一样了。
注意如果两数相等,那就没有先后关系,和第二题一样。
代码
T1
#include<bits/stdc++.h>
using ll = long long;
using ull = unsigned long long;
const int N=3e3+1,mod=1e9+7;
ll f[N][N],lf[N][N],rf[N][N];
void solve(){
int n;
std::string s;
std::cin>>n>>s;
s=" "+s;
f[1][1]=lf[1][1]=rf[1][1]=1;
for(int i=2;i<=n;i++){
if(s[i]=='<'){
for(int j=1;j<=i;j++)
f[i][j]=lf[i-1][j-1];
}
else{
for(int j=1;j<=i;j++)
f[i][j]=rf[i-1][j];
}
for(int j=1;j<=i;j++)
lf[i][j]=lf[i][j-1]+f[i][j],lf[i][j]%=mod;
for(int j=i;j>=1;j--)
rf[i][j]=rf[i][j+1]+f[i][j],rf[i][j]%=mod;
}
ll ans=0;
for(int i=1;i<=n;i++){
ans=(ans+f[n][i])%mod;
// std::cout<<f[n][i]<<"\n";
}
std::cout<<ans<<"\n";
}
int main(){
std::ios::sync_with_stdio(false),std::cin.tie(NULL),std::cout.tie(NULL);
int t=1;
// std::cin>>t;
while(t--)
solve();
return 0;
}
T2
void solve(){
int n;
std::string s;
while(std::cin>>s){
n=s.size()+1;
s=" "+s;
f[1][1]=lf[1][1]=rf[1][1]=1;
for(int i=2;i<=n;i++){
if(s[i]=='I'){
for(int j=1;j<=i;j++)
f[i][j]=lf[i-1][j-1];
}
else if(s[i]=='D'){
for(int j=1;j<=i;j++)
f[i][j]=rf[i-1][j];
}
else{
for(int j=1;j<=i;j++)
f[i][j]=(lf[i-1][j-1]+rf[i-1][j])%mod;
}
for(int j=1;j<=i;j++)
lf[i][j]=lf[i][j-1]+f[i][j],lf[i][j]%=mod;
for(int j=i;j>=1;j--)
rf[i][j]=rf[i][j+1]+f[i][j],rf[i][j]%=mod;
}
ll ans=0;
for(int i=1;i<=n;i++){
ans=(ans+f[n][i])%mod;
// std::cout<<f[n][i]<<"\n";
}
std::cout<<ans<<"\n";
}
}
T3
void solve(){
int n;
memset(f,-0x3f3f3f3f,sizeof f);
std::string s=" ";
std::cin>>n;
for(int i=1;i<=n;i++)
std::cin>>a[i],s+=" ";
for(int i=1;i<n;i++){
if(a[i]<a[i+1])
s[i]='<';
else if(a[i]>a[i+1])
s[i]='>';
else
s[i]='=';
}
s=" "+s;
f[1][1]=lf[1][1]=rf[1][1]=1;
for(int i=2;i<=n;i++){
if(s[i]=='<'){
for(int j=1;j<=i;j++)
f[i][j]=lf[i-1][j-1];
}
else if(s[i]=='>'){
for(int j=1;j<=i;j++)
f[i][j]=rf[i-1][j];
}
else{
for(int j=1;j<=i;j++)
f[i][j]=(lf[i-1][j-1]+rf[i-1][j])%mod;
}
for(int j=1;j<=i;j++)
lf[i][j]=lf[i][j-1]+f[i][j],lf[i][j]%=mod;
for(int j=i;j>=1;j--)
rf[i][j]=rf[i][j+1]+f[i][j],rf[i][j]%=mod;
}
ll ans=0;
for(int i=1;i<=n;i++){
ans=(ans+f[n][i])%mod;
// std::cout<<f[n][i]<<"\n";
}
std::cout<<ans<<"\n";
}