二次扫描与换根

二次扫描与换根

说实话,我很少为树型\(DP\)写文章,一来这是我最喜欢的一类\(DP\)了,二来是……(不知道要编啥了\(QAQ\)

不过这种方法极其有趣,让我有一种想写的欲望。

废话不多说,开题:

例题\(POJ3585\)

总结一下题意:无根树,流量,最大。

好吧讲真的我第一反应是最大流呵呵呵呵……

但是这个\(N\)极其不友好啊(\(200000\)你是要干啥?)

显然最大流是弄不了了,看一下我们似乎只用上了流量最大这两个条件无根树还没用上呢。

用树型\(DP\)的方法显然是可以的,但是如果我们暴力跑的话显然会\(TLE\)

暴力做法:每次以一个节点为根,跑一边树型\(DP\),最后取\(Max\)

再分析一下我们我们的暴力,发现我们并没有用到每一次\(DP\)的结果,相当于是浪费了时间。

那么我们能不能将前面的计算结果理由上呢?显然是可以的。

接下来就是我们的二次扫描与换根

V2Ii5t.png

如图:我们定义一个\(d[x]\),用于记录当我们选\(Root\)为全树的根时,以\(x\)为根的子树的流量\(Max\)

(我个人习惯将\(Root\)定义为\(1\),你们自己看着办。)

那么,我们就可以利用到先前跑出的答案了。

即有:\(f[y]=d[y]+min(f[x]-min(d[y],dis(x,y)),dis(x,y));\)

\(deg\)\(1\)时特判一下即可,上代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=200010;
int d[N],deg[N],f[N];
int n,num_edge,head[N];
struct Edge{int next,to,dis;} edge[N*2];//双向边,开两倍一定不能忘了!
inline void add(int from,int to,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].dis=dis;
    edge[num_edge].to=to;
    head[from]=num_edge;
}
inline void Work(int pos,int fa)
{
    for(int i=head[pos];i;i=edge[i].next)
        if(edge[i].to!=fa)
        {
            Work(edge[i].to,pos);
            if(deg[edge[i].to]==1) d[pos]+=edge[i].dis;
            else d[pos]+=min(d[edge[i].to],edge[i].dis);
        }
}
inline void Dfs(int pos,int fa)
{
    for(int i=head[pos];i;i=edge[i].next)
        if(edge[i].to!=fa)
        {
            if(deg[pos]==1) f[edge[i].to]=d[edge[i].to]+edge[i].dis;
            else f[edge[i].to]=d[edge[i].to]+min(f[pos]-min(d[edge[i].to],edge[i].dis),edge[i].dis);
            Dfs(edge[i].to,pos);
        }
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("Text1.in","r",stdin);
#endif
    int T=read();
    while(T--)
    {
        memset(f,0,sizeof(f));
        memset(d,0,sizeof(d));
        memset(deg,0,sizeof(deg));
        memset(head,0,sizeof(head));
        n=read();num_edge=0;
        for(int i=1,u,v,d;i<n;i++)
        {
            u=read(),v=read(),d=read();
            add(u,v,d),add(v,u,d);
            deg[u]++,deg[v]++;
        }
        int ans=0;
        Work(1,0);f[1]=d[1];Dfs(1,0);
        for(int i=1;i<=n;i++) ans=max(f[i],ans);
        printf("%d\n",ans);
    }
}

转载于:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11009599.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值