P4189 [CTSC2010]星际旅行 (退流思想)

P4189 [CTSC2010]星际旅行

分析:

对于只求一个点来说,因为题中保证了每个星球的hi大于等于度数。

也就是说,从一个点出发,保证可以遍历每一条边。

于是贪心地将能遍历到的边都遍历了,回溯的时候,将两个端点的h取min,累入答案里(在这两个点中重复走min次,贡献是min*2)

但题中要求每一个点。

考虑怎么利用已求出的点转移到未知的点

现在已知u,要推v。

1. h [ u ]>0 :从u走到v,ans++(原来是以u为终止点,现在改成v,如果u可以再出去的话,肯定要从u再走回v)

2. h [ u ]==0

这时候又分两种情况:

因为终点最后是v,所以v到u的流肯定是要改的,改成什么呢?

1.v的儿子节点h非0,将v ->u 改成 v->son[v]  son[v]->v,这样会使ans++

2.不能通过儿子使得ans++了,只能直接退掉这条边,ans - -。

#include<bits/stdc++.h>
using namespace std;
#define N 50005
#define ri register int
int h[N],son[N],n,tot=0,ans[N];
vector<int> e[N];
void dfs1(int u,int ff)
{
    for(ri i=0;i<e[u].size();++i){
        int v=e[u][i];
        if(v==ff) continue;
        dfs1(v,u);
        int tmp=min(h[u],h[v]);
        tot+=tmp*2; h[u]-=tmp; h[v]-=tmp;
        if(h[v]) son[u]=v;
    }
}
void dfs2(int u,int ff)
{
    ans[u]=tot;
    int fl=0;
    for(ri i=0;i<e[u].size();++i){
        int v=e[u][i];
        if(v==ff) continue;
        if(h[u]) fl=1,tot++,h[u]--;
        else if(son[v]) fl=2,tot++,h[son[v]]--;
        else fl=3,tot--,h[v]++;
        dfs2(v,u);
        if(fl==1) tot--,h[u]++;
        else if(fl==2) tot--,h[son[v]]++;
        else tot++,h[v]--;
    }
}
int main()
{
    scanf("%d",&n);
    for(ri i=1;i<=n;++i) scanf("%d",&h[i]);
    for(ri i=1;i<=n-1;++i){
        int a,b;
        scanf("%d%d",&a,&b);
        a++,b++;
        e[a].push_back(b),e[b].push_back(a);
        h[a]--; h[b]--; tot+=2;//相当于遍历一遍 每条边都参与了2的贡献 
    } 
    dfs1(1,0);
    dfs2(1,0);
    for(ri i=1;i<=n;++i) printf("%d
",ans[i]);
}
View Code
原文地址:https://www.cnblogs.com/mowanying/p/11808638.html