dp杂题选做

2023-07-29,

树的数量

题目其实挺简单的,难点在于状态的设计(其实也没多难)。

令 \(f_i\) 表示 \(i\) 个点的 \(m\) 叉树的数量,发现无法转移。设 \(g_{i,j}\) 表示根节点所在子树内有 \(i\) 个节点,\(j\) 个儿子(儿子所在子树可以为空)。可以写出转移方程:\(g_{i,j}=\sum\limits_{k=0}^{i-1} g_{i-k,j-1}\times f_k\),\(f_i=g_{i,m}\)

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pdi pair<double,int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define eps 1e-9
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=200,mod=1e4+7;
int n,m,f[N][N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
read(n),read(m);
for(int i=0;i<=m;i++){
f[1][i]=f[0][i]=1;
}
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<i;k++){
f[i][j]=(f[i][j]+f[i-k][j-1]*f[k][m]%mod)%mod;
}
}
}
write_endl(f[n][m]);
return 0;
}

[SCOI2015]小凸玩密室

个人觉得这题非常神秘。

根据题目可知每次访问操作是访问完自己的子树,再访问父亲。所以除了第一次任意选点,剩下的都是从某个叶子节点跳到它的一个祖先或者往它的某棵子树跳。显然一颗子树对于后面的影响在于遍历完这颗子树后停在了哪里。

令 \(f_{u,x}\) 表示从根开始,访问完以 \(u\) 为根的子树后停在 \(x\) 处的最小费用,\(ls/rs\) 表示左/右儿子。分类讨论可以得到长得极为对称的两个转移方程

当 \(x\) 在左子树,\(f_{u,y}=\min\{dis(u,ls)\times a_{ls}+f_{ls,x}+dis(x,rs)\times a_{rs}+f_{rs,y}\}\)

当 \(x\) 在右子树,\(f_{u,y}=\min\{dis(u,rs)\times a_{rs}+f_{rs,x}+dis(x,ls)\times a_{ls}+f_{ls,y}\}\)

这个方程的复杂度接近 \(O(n^2)\),显然不能接受。

拆开式子,\(dis(x,rs)=dis(u,rs)+dis(x,u)\),发现只有 \(dis(x,u)\) 是个影响答案的关键信息,将它记录下来。

但因为没有要求从 \(1\) 开始,所以定义另一个状态 \(g_{u,x}\) 表示访问完以 \(u\) 为根的子树后停在 \(x\) 处的最小费用。继续分类讨论

当 \(x\) 在左子树,\(g_{u,y}=\min\{f_{u,y},f_{ls,x}+dis(x,u)\times a_u+dis(rs,u)\times a_{rs}+f_{rs,y}\}\)

当 \(x\) 在右子树,\(g_{u,y}=\min\{f_{u,y},f_{rs,x}+dis(x,u)\times a_u+dis(ls,u)\times a_{ls}+f_{ls,y}\}\),最后从所有 \(g_{1,x}\) 中取最小值即可。

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pdi pair<double,int>
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define eps 1e-9
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=2e5+10,inf=1e18;
int dis[N],n,a[N],b[N];
vector<int>d[N],f[N],g[N];
int ls(int p){
return p<<1;
}
int rs(int p){
return p<<1|1;
}
int fa(int p){
return p>>1;
}
void dfs(int u){
dis[u]=dis[fa(u)]+b[u];
if(ls(u)<=n){
dfs(ls(u));
int s=f[ls(u)].size();
if(rs(u)<=n){
dfs(rs(u));
int ans1=inf,ans2=inf,ansp1=inf,ansp2=inf;
for(int i=0;i<f[u].size();i++){
if(i<s){
ans1=min(ans1,f[ls(u)][i]+b[ls(u)]*a[ls(u)]+(d[u][i]+b[rs(u)])*a[rs(u)]);
ansp1=min(ansp1,g[ls(u)][i]+d[u][i]*a[u]+b[rs(u)]*a[rs(u)]);
}
else{
ans2=min(ans2,f[rs(u)][i-s]+b[rs(u)]*a[rs(u)]+(d[u][i]+b[ls(u)])*a[ls(u)]);
ansp2=min(ansp2,g[rs(u)][i-s]+d[u][i]*a[u]+b[ls(u)]*a[ls(u)]);
}
}
for(int i=0;i<f[u].size();i++){
if(i<s){
f[u][i]=ans2+f[ls(u)][i];
g[u][i]=min(f[u][i],ansp2+f[ls(u)][i]);
}
else{
f[u][i]=ans1+f[rs(u)][i-s];
g[u][i]=min(f[u][i],ansp1+f[rs(u)][i-s]);
}
}
}
else{
for(int i=u;i>=1;i/=2){
f[i].pb(0);
g[i].pb(0);
d[i].pb(dis[u]-dis[i]); }
f[u][0]=b[ls(u)]*a[ls(u)];
g[u][0]=inf;
f[u][1]=inf;
g[u][1]=b[ls(u)]*a[u];
}
}
else{
for(int i=u;i>=1;i/=2){
f[i].pb(0);
g[i].pb(0);
d[i].pb(dis[u]-dis[i]);
}
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
read(n);
for(int i=1;i<=n;i++){
read(a[i]);
}
for(int i=2;i<=n;i++){
read(b[i]);
}
dfs(1);
int ans=inf;
for(int i=0;i<g[1].size();i++){
ans=min(ans,g[1][i]);
}
write_endl(ans);
return 0;
}

dp杂题选做的相关教程结束。

《dp杂题选做.doc》

下载本文的Word格式文档,以方便收藏与打印。