【hdu2196】Computer

来源:互联网 发布:hpm128fw扫描软件 编辑:程序博客网 时间:2024/05/20 22:02

hdu 2196 computer

题意

给你一棵树,边有权值。
对于每一个点,求其与其距离最远的点的距离。
n10000

分析

思路1:树的直径

利用直径的性质进行求解,网上资料很多,这里不赘述。

#include <cstdio>#include <cstring>#include <cctype>#include <algorithm>using namespace std;const int N=16384;int n;struct G{    int v,d,nxt;}mp[N<<1];int tt,hd[N];int dis[N];int vis[N];int mx[N];inline int read(void){    int x=0; char c=getchar();    for (;!isdigit(c);c=getchar());    for (;isdigit(c);c=getchar()) x=x*10+c-'0';    return x;}inline void ins(int u,int v,int d){    mp[++tt].v=v;    mp[tt].d=d;    mp[tt].nxt=hd[u];    hd[u]=tt;}void dfs(int now){    vis[now]=1;    for (int k=hd[now];k;k=mp[k].nxt)        if (!vis[mp[k].v])        {            dis[mp[k].v]=dis[now]+mp[k].d;            dfs(mp[k].v);        }}int main(void){//  freopen("a.in","r",stdin);//  freopen("a.out","w",stdout);    while (scanf("%d",&n)!=EOF)    {        int x,y;        memset(hd,0,sizeof hd); tt=0;        for (int i=2;i<=n;i++)        {            x=read(),y=read();            ins(i,x,y),ins(x,i,y);        }        int tmp,tvt;        memset(mx,0,sizeof mx);        memset(dis,0,sizeof dis);        memset(vis,0,sizeof vis);        dfs(1);        for (int i=1;i<=n;i++)            mx[i]=max(mx[i],dis[i]);        tvt=1,tmp=mx[1];        for (int i=2;i<=n;i++)            if (tmp<mx[i]) tvt=i,tmp=mx[i];        memset(dis,0,sizeof dis);        memset(vis,0,sizeof vis);        dfs(tvt);        for (int i=1;i<=n;i++)            mx[i]=max(mx[i],dis[i]);        tvt=1,tmp=mx[1];        for (int i=2;i<=n;i++)            if (tmp<mx[i]) tvt=i,tmp=mx[i];        memset(dis,0,sizeof dis);        memset(vis,0,sizeof vis);        dfs(tvt);        for (int i=1;i<=n;i++)            mx[i]=max(mx[i],dis[i]);        for (int i=1;i<=n;i++)            printf("%d\n",mx[i]);    }    return 0;}

思路2:二次树形dp

我们要求每个点与其最远点的距离。
我们随便选择一个点构造一棵有根树,那么对于任意一个点x,它的直径无非两种情况:
①点xx的子树内延伸最长的长度
②点xx的子树外延伸最长的长度

对于第一种情况,我们需要记录fx[i]:从点i往下延伸的最长长度
fx[i]=max(fx[son]+mp[k].d)

对于第二种情况,我们需要记录ux[i]:从点i往外延伸的最长长度
i的父亲为pre,一种情况是在点pre时弯了,另一种情况是点pre往外延伸。
所以ux[i]=max(ux[pre],fx[son]+dis(pre,son)),soni

然后需要优化处理。
方法1:从前往后扫一遍,从后往前扫一遍。
方法2:记录最大值和次大值,若i是pre的最大值,那么就取次大,否则取最大。

#include <cstdio>#include <cstring>#include <cctype>#include <algorithm>using namespace std;#define rep(i,a,b) for (int i=(a);i<=(b);i++)#define per(i,a,b) for (int i=(a);i>=(b);i--)#define fore(k,u) for (int k=hd[u];k>0;k=mp[k].nx)#define ed lis[i]const int N=16384;const int E=32768;int n;struct G{    int v,d;    int nx;    inline G(int _v=0,int _d=0,int _nx=0)    {        v=_v,d=_d;        nx=_nx;    }}mp[E];int tot,hd[N];int fx[N];int ux[N]; int lis[E],len;int ans[N];inline int rd(void){    int x=0,f=1; char c=getchar();    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;    for (;isdigit(c);c=getchar()) x=x*10+c-'0';    return x*f;}inline void Ins(int u,int v,int d){    mp[++tot]=G(v,d,hd[u]); hd[u]=tot;    mp[++tot]=G(u,d,hd[v]); hd[v]=tot;}void FirDFS(int now,int pre){    fore(k,now) if (mp[k].v!=pre)        FirDFS(mp[k].v,now);    fore(k,now) if (mp[k].v!=pre)        fx[now]=max(fx[now],fx[mp[k].v]+mp[k].d);}void SecDFS(int now,int pre){    len=0;    fore(k,now) if (mp[k].v!=pre)        lis[++len]=k;    if (~pre)    {        rep(i,1,len)            ux[mp[ed].v]=max(ux[mp[ed].v],ux[now]);    }    int mx=0;    rep(i,1,len)    {        ux[mp[ed].v]=max(ux[mp[ed].v],mx);        mx=max(mx,fx[mp[ed].v]+mp[ed].d);    }    mx=0;    per(i,len,1)    {        ux[mp[ed].v]=max(ux[mp[ed].v],mx);        mx=max(mx,fx[mp[ed].v]+mp[ed].d);    }    rep(i,1,len)        ux[mp[ed].v]+=mp[ed].d;    fore(k,now) if (mp[k].v!=pre)        SecDFS(mp[k].v,now);}int main(void){//  freopen("hdu2196.in","r",stdin);//  freopen("hdu2196.out","w",stdout);    while (~scanf("%d",&n))    {        tot=0; memset(hd,0,sizeof hd);        rep(i,2,n)        {            int v=rd(),d=rd();            Ins(i,v,d);        }        memset(fx,0,sizeof fx);        memset(ux,0,sizeof ux);        FirDFS(1,-1);        SecDFS(1,-1);        memset(ans,0,sizeof ans);        rep(i,1,n) ans[i]=max(fx[i],ux[i]);        rep(i,1,n) printf("%d\n",ans[i]);    }    return 0;}

小结

(1)树的常见方法
①树形dp
②点分治
③树链剖分,dfs序剖分:树–>链
④树上倍增
⑤树上莫队
⑥LCT
当然还要用一些什么树的直径之类的性质。

0 0
原创粉丝点击