洛谷P4438 道路 [HNOI/AHOI2018] 树形dp

正解:树形dp

解题报告:

传送门!

昂首先看懂题目趴QwQ大概就是说有棵满二叉树,有n个叶子节点(乡村)和n-1个非叶子节点,然后这棵树的每个节点有三个属性abc,对每个非叶子节点可以从与子节点的两条连边中选一条标记

然后对每个叶子节点i,设它到根节点经过了x条麻油被标记的左连边,y条麻油被标记的右连边,那它的贡献就会是ci*(ai-x)*(bi-y)

然后就考虑dp鸭

设f[x][i][j]:对于节点x,到达根的路径上有i条麻油标记的左连边,j条麻油标记的右连边

然后初始化是对叶子节点,直接枚举i和j

然后转移就是对非叶子节点,可以标记左连边可以标记右连边,就f[x][i][j]=min(f[x.ls][i][j]+f[x.rs][i][j+1],f[x.ls][i+1][j]+f[x.rs][i][j])

答案就是f[1][0][0]嘛

然后这题就做完辣!看起来好简单的样子

另外就是,这道题如果开的是f[N][40][40]显然是会爆空间的QAQ所以要想下怎么卡空间QAQ

然后仔细思考一下,因为这是一棵树,而且转移一定是从根dfs到儿子节点的,所以当两个儿子节点都访问完之后一定不会再回来辣

所以可以用,记录dfn的方式,这里的dfn并不是真正每次都++的那种(不然完全麻油省空间鸭QAQ),它是指的dfn[x.ls]=dfn[x]+1,dfn[x.rs]=dfn[x]+2,显然这样是可以保证正确性的,然后这样就能保障f的第一维<=2*40+1,这样就肯定不会爆空间辣!

然后记得它其实是有2*n-1个节点的,所以dfn序的那个数组要开两倍N,,,

虽然我jio得不会有人和我一样犯这种sd错误辣?

而且我居然RE了两三次才发现是这个问题,,,吃枣药丸TT

然后放下代码就溜辣!

洛谷P4438 道路 [HNOI/AHOI2018] 树形dp洛谷P4438 道路 [HNOI/AHOI2018] 树形dp
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define ll long long
#define gc getchar()
#define rp(i,x,y) for(rg int i=x;i<=y;++i)
#define my(i,x,y) for(rg int i=x;i>=y;--i)

const int N=40000+10;
int n,m,dfn[N];
long long f[110][50][50];
struct node{int g,t;}nod[N];
struct leaf{int a,b,c;}lf[N];

il int read()
{
    rg char ch=gc;rg int x=0;rg bool y=1;
    while(ch!='-' && (ch<'0' || ch>'9'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il void dfs(int x,int nw,int gl,int tl)
{
    dfn[x]=nw;
    if(x>=n){rp(i,0,gl)rp(j,0,tl)f[dfn[x]][i][j]=1ll*lf[x].c*1ll*(lf[x].a+i)*(lf[x].b+j);return;}
    dfs(nod[x].g,nw+1,gl+1,tl);dfs(nod[x].t,nw+2,gl,tl+1);
    rp(i,0,gl)rp(j,0,tl)f[dfn[x]][i][j]=min(f[nw+1][i+1][j]+f[nw+2][i][j],f[nw+1][i][j]+f[nw+2][i][j+1]);
}

int main()
{
//    freopen("dl.in","r",stdin);freopen("dl.out","w",stdout);
    n=read();
    rp(i,1,n-1){int s=read(),t=read();if(s>0)nod[i].g=s;else nod[i].g=n-s-1;if(t>0)nod[i].t=t;else nod[i].t=n-t-1;}rp(i,0,n-1)lf[i+n].a=read(),lf[i+n].b=read(),lf[i+n].c=read();
    dfs(1,1,0,0);printf("%lld\n",f[1][0][0]);
    return 0;
}
View Code

嗷对了这题还有一个解法来着QwQ

只是不管是时间上还是空间上都不太优秀,而且非常好想,就很适合我这种菜菜,然后居然能过,实在是很友好了TT

就直接无脑记搜就好,放下代码就能get了应该QAQ?

洛谷P4438 道路 [HNOI/AHOI2018] 树形dp洛谷P4438 道路 [HNOI/AHOI2018] 树形dp
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define ll long long
#define gc getchar()
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define my(i,x,y) for(rg ll i=x;i>=y;--i)

const ll N=20000+10;
ll n,m,f[N][43][43];
struct node{ll ls,rs;}nod[N];
struct leaf{ll a,b,c;}lf[N];

il ll read()
{
    rg char ch=gc;rg ll x=0;rg bool y=1;
    while(ch!='-' && (ch<'0' || ch>'9'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while('0'<=ch && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il ll dfs(ll x,ll i,ll j)
{
    if(x>=n)return lf[x-n].c*(lf[x-n].a+i)*(lf[x-n].b+j);if(f[x][i][j]!=f[n+1][41][41])return f[x][i][j];
    return f[x][i][j]=min(dfs(nod[x].ls,i,j)+dfs(nod[x].rs,i,j+1),dfs(nod[x].ls,i+1,j)+dfs(nod[x].rs,i,j));
}

int main()
{
//    freopen("dl.in","r",stdin);
    n=read();rp(i,1,n-1){ll s=read(),t=read();if(s>0)nod[i].ls=s;else nod[i].ls=n-s-1;if(t>0)nod[i].rs=t;else nod[i].rs=n-t-1;}rp(i,0,n-1)lf[i].a=read(),lf[i].b=read(),lf[i].c=read();
    memset(f,63,sizeof(f));printf("%lld\n",dfs(1,0,0));
    return 0;
}
View Code