[HNOI/AHOI2018]道路

P4438 [HNOI/AHOI2018]道路

题意:

从叶子节点到根节点,会经过 \(i\) 条烂公路 \(j\) 条烂铁路。

每个叶子节点的贡献为 \(c[i]*(a[i]+i) * (b[i]+j)\)

我们可以翻修 \(n-1\) 条路,求 \(\sum_{叶子节点 i} c[i]*(a[i]+i) * (b[i]+j)\) 最小。

解决:

\(f[x][i][j]\) 为从根节点到节点 \(x\) ,经过 \(i\) 公路, \(j\) 铁路

因为有左儿子,右儿子的存在,所以我们通过 \(dfs\)\(dfn\) 进行标号,从而使 \(dp\) 更方便。

叶子节点 进行预处理:

\[dp[dfn[x]][i][j]=c[x]*(a[x]+i)*(b[x]+j); \]

如果选择修公路,右儿子经过一条未修的铁路:

\[dp[dfn[x]][i][j]=dp[dfn[son[x][0]]][i][j]+dp[dfn[son[x][1]]][i][j+1] \]

如果选择修铁路,左儿子经过一条未修的公路:

\[dp[dfn[x]][i][j]=dp[dfn[son[x][0]]][i+1][j]+dp[dfn[son[x][1]]][i][j] \]

一起取 \(min\) ,顺序进行枚举即可算出答案。

#include<bits/stdc++.h>
using namespace std;
#define int long long 

const int N=1e5+5,M=2e4+5;

int n,m;
int nxt[N],ver[N],tot,head[N];
int a[N],b[N],c[N];
int dp[105][45][45];
int dfn[N],sizes[N],dep[N];
vector<int> son[M];

void add(int x,int y){
    ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;
}

void dfs(int x,int k){
    dfn[x]=k; //通过dfs序排节点
    sizes[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i]; 
        dep[y]=dep[x]+1;
        son[x].push_back(y); 
        if(son[x][1]) dfs(y,k+2);
        else          dfs(y,k+1);
        sizes[x]+=sizes[y];
    }
    if(sizes[x]==1) //叶子节点,乡村预处理
        for(int i=0;i<=dep[x];i++) for(int j=0;j<=dep[x];j++) 
            dp[dfn[x]][i][j]=c[x]*(a[x]+i)*(b[x]+j);
    else 
        for(int i=0;i<=dep[x];i++) for(int j=0;j<=dep[x];j++) //选择修公路,右儿子多经过一条未修的道路,修铁路,左儿子经过一条未修的公路,取最小值即可。
        dp[dfn[x]][i][j]=min(dp[dfn[son[x][0]]][i][j]+dp[dfn[son[x][1]]][i][j+1],dp[dfn[son[x][0]]][i+1][j]+dp[dfn[son[x][1]]][i][j]);
}

signed main(){
    cin>>n; m=2*n-1; memset(dp,0x3f3f3f3f,sizeof(dp));
    for(int i=1,x,y;i<n;i++){
        scanf("%lld%lld",&x,&y); 
        if(y>=0) add(i,y); else add(i,-y+n-1);
        if(x>=0) add(i,x); else add(i,-x+n-1);
    }
    for(int i=n;i<=m;i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
    dep[1]=0;
    dfs(1,0);
    printf("%lld\n",dp[dfn[1]][0][0]);
    system("pause");
    return 0;
}
posted @ 2021-09-22 17:32  Evitagen  阅读(30)  评论(0编辑  收藏  举报