【题解】AT_dp_t Permutation & UVA1650 数字串 Number String & AT_abc209_f [ABC209F] Deforestation

AT_dp_t Permutation

link

题意

有一个长为 N N N 的正整数排列。给定一个由 <> 组成长为 N − 1 N-1 N1 的的字符串。
对于任意满足 1 ≤ i ≤ N − 1 1 \le i \le N-1 1iN1 的字符 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 Pi1<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=1j1f[i1][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[i1][k]

然后你就发现样例都过不了。

重新审视转移方程。

我们发现:

P i − 1 < P i P_{i-1}<P_i Pi1<Pi:那 P [ i − 1 ] P[i-1] P[i1] 只能取比 P [ i ] P[i] P[i] 小的,共 j − 1 j-1 j1 个。
但是反过来,你却发现一共有 i − j + 1 i-j+1 ij+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]={pi1<pipi1>pik=1j1f[i1][j]k=jif[i1][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]= pi1<pipi1>pipi1=pik=1j1f[i1][j]k=jif[i1][j]k=1j1f[i1][j]+k=jif[i1][j]

注意多测。

AT_abc209_f [ABC209F] Deforestation

link

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} ai1+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[i1]+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[i1]+a[i]

w 1 < w 2 → a [ i + 1 ] < a [ i ] w_1<w_2 \rightarrow a[i+1]<a[i] w1<w2a[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";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值