【总结】树形dp

  Two

  求用两个人从根开始走完整个树所用的最短路程.

  情况分为两种:

  1.两个人从根分开走并不再会和;

  2.让一个人走完其它子树,并两个人会和在未走的子树的根,得到一个子问题.

  要用到一个结论:

    从一个点遍历整棵树走的最小路径=树边长和*2-树直径.

two
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)
#define maxn 100010
using namespace std;
const int inf=500000001;
typedef struct nd
{
    int v;
    int d;
}nd;
vector<nd>e[maxn];
int n,s;
int mxl[maxn];
int td[maxn];
int dp[maxn];
void input()
{
    int i;
    int a,b,c;
    nd tmp;
    for(i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        tmp.v=b;tmp.d=c;
        e[a].push_back(tmp);
        tmp.v=a;tmp.d=c;
        e[b].push_back(tmp);
    }
}
void init()
{
    int i;
    for(i=1;i<=n;i++)
    e[i].clear();
    memset(mxl,-1,sizeof(mxl));
    memset(td,-1,sizeof(td));
    memset(dp,-1,sizeof(dp));
}
int init_td(int fa,int cur)
{
    int i,res=0;
    nd ck;
    for(i=0;i<e[cur].size();i++)
    if(e[cur][i].v!=fa)
    {
        ck=e[cur][i];
        res+=init_td(cur,e[cur][i].v)+e[cur][i].d;
    }
    return td[cur]=res;
}
int init_mxl(int fa,int cur)
{
    int i,res=0;
    for(i=0;i<e[cur].size();i++)
    if(e[cur][i].v!=fa)
    {
        int tmp=init_mxl(cur,e[cur][i].v);
        res=max(res,tmp+e[cur][i].d);
    }
    return mxl[cur]=res;
}
bool cmp(const nd& a, const nd& b){
    return mxl[a.v]+a.d > mxl[b.v]+b.d;
}
int dfs(int fa,int cur)
{
    int i,res,cut=0;
    nd ck;
    res=td[cur]*2-mxl[cur];
    for(i=0;i<e[cur].size();i++)
    if(e[cur][i].v != fa)
    {
        ck=e[cur][i];
        int t=td[cur]-td[e[cur][i].v]-e[cur][i].d;
        int tmp=dfs(cur,e[cur][i].v)+t*2+e[cur][i].d*2;
        res=min(res,tmp);
    }
    for(i=0;i<e[cur].size();i++)
    if(e[cur][i].v != fa)
    {
        cut+=mxl[e[cur][i].v]+e[cur][i].d;
        break;
    }
    for(i++;i<e[cur].size();i++)
    if(e[cur][i].v != fa)
    {
        cut+=mxl[e[cur][i].v]+e[cur][i].d;
        break;
    }
    res=min(td[cur]*2-cut,res);
    return dp[cur]=res;
}
void solv()
{
    int i;
    init_td(0,s);
    init_mxl(0,s);
    for(i=1;i<=n;i++)
    sort(e[i].begin(),e[i].end(),cmp);
    dfs(0,s);
    printf("%d\n",dp[s]);
}
int main()
{
    while(scanf("%d%d",&n,&s)!=EOF)
    {
        init();
        input();
        solv();
    }
    return 0;
}

  

  Binary Apple Tree

  给一个二叉树,每个点有它的权值,剪去一些边保留q条边,求最大能保留多少权值.(要保证剪完后仍然是一颗树)

  这是个简单的问题:

  dp[i][j]为以i为根,保留j条边的最大权值.

  dp[i][j]=max{dp[ls][t]+dp[rs][j-2-t]}

  但是非常具有启发性:

  如果将二叉树换为普通树,将得到问题的拓展版.

  假设普通树有k个孩子,可以将问题看做容量为q,物品数为k*q的01背包

  

树形背包
 1 void dfs(int fa,int cur)
 2 {
 3     int i,j,k;
 4     for( i=0; i<e[cur].size();i++)
 5     if(e[cur][i] != fa){
 6         int v=e[cur][i];
 7         dfs(cur,v);
 8         for(j=q;j>0;j--)
 9         for(k=0;k<j;k++)
10             dp[cur][j]=max(dp[cur][j],dp[cur][j-k-1]+dp[v][k]);
11     }
12 }
13 /*
14     注意更新dp[][]的两层循环不能颠倒成
15     for(k=0;k<q;k++)
16     for(j=q;j>0;j--)
17     if(j-k>=0)
18         dp[cur][j]=max(dp[cur][j],dp[cur][j-k-1]+dp[v][k]);
19     因为dp【cur][j-k-1]更新dp[cur][j]的时候,可能已经被dp[v][]更新过了,
20     于是dp[v][]被加过两次.
21 */

 

  因为遍历树是有顺序的,遍历到u的儿子vn时v1~vn-1都已经用来更新过dp[u][]而vn及其以后的儿子都没有用来更新,

  所以这样dp是清楚的,不会产生混乱和重复.

 

  Apple Tree

 

  给一个带权的普通树,求走k步能获得的最大权值.

  将dp分为两部分:

  dp[0][i][j]从i点出发走j步并回来获得的最大权值

  dp[1][i][j]从i点出发走j步不回来能获得的最大权值.

  然后用类似 Binary Apple Tree 的方式更新.

  

Apple tree
#include<stdio.h>
#include<string.h>
#include<vector>
#define maxn 222
#define max(a,b) (a)>(b)?(a):(b)
using namespace std;
vector<int> e[maxn];
int n,m,d[2][maxn][maxn],val[maxn],ans=-1;
void input()
{
    int i,u,v;
    for(i=1;i<=n;i++)scanf("%d",&val[i]);
    for(i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
}
void DP(int fa,int cur,int maxm)
{
    int i,j,k,v;
    d[0][cur][0]=val[cur],d[1][cur][0]=val[cur];
    for(i=0;i<e[cur].size();i++)
    if(e[cur][i]!=fa){
        v=e[cur][i];
        DP(cur,v,maxm-1);
        for(j=maxm;j>=0;j--)
        for(k=0;k<j;k++){
            if(j-2-k>=0 && d[0][cur][j-k-2]>=0 && d[0][v][k]>=0)
            d[0][cur][j]=max(d[0][cur][j],d[0][cur][j-2-k]+d[0][v][k]);

            if(j-1-k>=0 && d[1][v][k]>=0 && d[0][cur][j-1-k]>=0)
            d[1][cur][j]=max(d[1][cur][j],d[1][v][k]+d[0][cur][j-1-k]);

            if(j-2-k>=0 && d[1][cur][j-2-k]>=0 && d[0][v][k]>=0)
            d[1][cur][j]=max(d[1][cur][j],d[1][cur][j-2-k]+d[0][v][k]);
        }
    }
}
void flush()
{
    int i;
    for(i=0;i<=n;i++)
        e[i].clear();
}
int main()
{
    //freopen("test.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        input();
        memset(d,-1,sizeof(d));
        ans=-1;
        DP(0,1,m);
        for(int i=0;i<=m;i++)for(int j=0;j<2;j++){
            ans=max(ans,d[j][1][i]);
        }
        printf("%d\n",ans);
        flush();
    }
    return 0;
}

 

  Network

  给n个点的树,和m条新加的边,问有多少种方式,删掉原树的一条边和新加的一条边使图分为两部分.

  每加上一条新边,都会产生环,如果原树中有一条边只在一个环中,那么删掉它和对应的新边就会使图分为两部分.

  当然,还要统计出原树中未成为环的边,因为只需删掉它们中一条就可以使图分为两部分.

  统计的方式十分巧妙,用dp[v]表示点v到他父亲的边,如果加入的边为e(u,v),

  那么  dp[u]++,  dp[v]++,  dp[lca(u,v)]-=2;  最后深度遍历一遍,得到所有边的值.

  其实仔细想想也不难发现它的正确性.

  这题的重点是发现分类标准,和找到统计方式.

  

Network
  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<vector>
  4 #include<math.h>
  5 #define maxn 100100
  6 using namespace std;
  7 struct edge{
  8     int v,nxt;
  9 };edge e[maxn*2];
 10 int first[maxn*2],ne;
 11 int dgr[maxn*2],idx[maxn],nd[maxn*2],sz,rmq[maxn*2][20],vis[maxn];
 12 int f[maxn],n,m;
 13 
 14 void add_edge(int u,int v){
 15     e[ne].v=v;e[ne].nxt=first[u];
 16     first[u]=ne++;
 17 }
 18 void input()
 19 {
 20     int i,v,u;
 21     ne=0;
 22     memset(first,-1,sizeof(first));
 23     for(i=1;i<n;i++){
 24         scanf("%d%d",&u,&v);
 25         add_edge(u,v);
 26         add_edge(v,u);
 27     }
 28 }
 29 void dfs(int cur,int deep)
 30 {
 31     int i;
 32     dgr[++sz]=deep;nd[sz]=cur;
 33     idx[cur]=sz;
 34     for(i=first[cur];i!=-1;i=e[i].nxt)
 35     if(!vis[e[i].v]){
 36         vis[e[i].v]=1;
 37         dfs(e[i].v,deep+1);
 38         dgr[++sz]=deep;    nd[sz]=cur;
 39     }
 40 }
 41 void init_rmq()
 42 {
 43     int i,j,lmt,lft,rgt;
 44     for(i=1;i<=sz;i++)rmq[i][0]=i;
 45     for(i=1;(1<<i)<=sz;i++)
 46     for(j=1;j+(1<<i)-1<=sz;j++)
 47     {
 48         rmq[j][i]=rmq[j][i-1];
 49         lft=rmq[j][i-1];
 50         rgt=rmq[j+(1<<(i-1))][i-1];
 51         if(dgr[lft]<dgr[rgt])rmq[j][i]=lft;
 52         else    rmq[j][i]=rgt;
 53     }
 54 }
 55 int LCA(int l,int r)
 56 {
 57     int rg,k,lft,rgt;
 58     if(l>r)swap(l,r);
 59     rg=r-l+1;
 60     k=log((double)rg)/log(2.0);
 61     lft=rmq[l][k];
 62     rgt=rmq[r-(1<<k)+1][k];
 63     if(dgr[lft]<dgr[rgt])return nd[lft];
 64     return nd[rgt];
 65 }
 66 int statis(int cur)
 67 {
 68     int i;
 69     for(i=first[cur];i!=-1;i=e[i].nxt)
 70     if(!vis[e[i].v]){
 71         vis[e[i].v]=1;
 72         f[cur]+=statis(e[i].v);
 73     }
 74     return f[cur];
 75 }
 76 
 77 int main()
 78 {
 79     int u,v,lca;
 80     int ans;
 81     //freopen("test","r",stdin);
 82     scanf("%d%d",&n,&m);
 83     input();
 84     sz=0;
 85     memset(vis,0,sizeof(vis));vis[1]=1;
 86     dfs(1,0);
 87     init_rmq();
 88     memset(f,0,sizeof(f));
 89     for(int i=0;i<m;i++){
 90         scanf("%d%d",&u,&v);
 91         lca=LCA(idx[u],idx[v]);
 92         f[u]++;f[v]++;f[lca]-=2;
 93     }
 94     memset(vis,0,sizeof(vis));vis[1]=1;
 95     statis(1);
 96     ans=0;
 97     for(int i=2;i<=n;i++)
 98     if(f[i]==0 || f[i]==1){
 99         if(f[i]==0)ans+=m;
100         if(f[i]==1)ans++;
101     }
102     printf("%d\n",ans);
103     return 0;
104 }

 

  Journey

  问题可以描述为:旅人在树的k点,要访问一系列点,求最小路程.

  造树就行了,方法很多,我是用bfs不断找要访问点的父亲,也可以dfs一遍用dp[v]表示从v点下去是否有需要访问的点.

  这题很恶心地卡了stl,害我wa了8次,看来以后使用stl的时候要小心.

  

Journey
  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<queue>
  4 #include<vector>
  5 #define maxn 55555
  6 using namespace std;
  7 struct edge{
  8     int v,nxt;
  9 };
 10 edge e[maxn*2],t[maxn*2];
 11 int he[maxn*2],ht[maxn*2],ne,nt,d1[maxn*2],d2[maxn*2];
 12 
 13 int n,k,vis[maxn],fa[maxn],rcd[maxn],bg,len[maxn],total;
 14 queue<int>q;
 15 void add_edge_e(int u,int v,int dist)
 16 {
 17     e[ne].v=v;e[ne].nxt=he[u];d1[ne]=dist;
 18 //    printf("d1[%d]=%d\n",ne,d1[ne]);
 19     he[u]=ne++;
 20 }
 21 void add_edge_t(int u,int v,int dist)
 22 {
 23     t[nt].v=v;t[nt].nxt=ht[u];d2[nt]=dist;
 24 //    printf("d2[%d]=%d\n",nt,d2[nt]);
 25     ht[u]=nt++;
 26 }
 27 void input()
 28 {
 29     int i,u,v,dist;
 30     memset(he,-1,sizeof(he));ne=0;
 31     memset(ht,-1,sizeof(ht));nt=0;
 32     for(i=1;i<n;i++){
 33         scanf("%d%d%d",&u,&v,&dist);
 34         add_edge_e(u,v,dist);
 35         add_edge_e(v,u,dist);
 36     }
 37     scanf("%d",&k);
 38     for(i=1;i<=k;i++)scanf("%d",&vis[i]);
 39 }
 40 void dfs(int f,int cur)
 41 {
 42     int i;
 43     fa[cur]=f;
 44     for(i=he[cur];i!=-1;i=e[i].nxt)
 45     if(e[i].v!=f){
 46 //        printf("fa=%d cur=%d dist=%d\n",cur,e[cur][i],d1[cur][i]);
 47         dfs(cur,e[i].v);
 48     }
 49 }
 50 int find_dist(int cur,int fa_cur)
 51 {
 52     int i;
 53     for(i=he[fa_cur];i!=-1;i=e[i].nxt){
 54 //        printf("e[%d].v=%d cur=%d\n",i,e[i].v,cur);
 55         if(e[i].v==cur){
 56             return d1[i];
 57         }
 58     }
 59     return -1;
 60 }
 61 void bfs()
 62 {
 63     int i,cur,nxt,dist;
 64     memset(rcd,0,sizeof(rcd));
 65     rcd[bg]=1;
 66     for(i=1;i<=k;i++){
 67         q.push(vis[i]);
 68         rcd[vis[i]]=1;
 69     }
 70     while(!q.empty()){
 71         cur=q.front();q.pop();
 72         nxt=fa[cur];
 73         dist=find_dist(cur,nxt);
 74         total+=dist;
 75     //    printf("fa=%d cur=%d dist=%d\n",nxt,cur,dist);
 76         add_edge_t(nxt,cur,dist);
 77         if(!rcd[nxt]){
 78             rcd[nxt]=1;
 79             q.push(nxt);
 80         }
 81     }
 82 }
 83 void init_len(int cur)
 84 {
 85     int i;
 86     for(i=ht[cur];i!=-1;i=t[i].nxt){
 87         init_len(t[i].v);
 88         len[cur]=max(len[cur],len[t[i].v]+d2[i]);
 89     }
 90 }
 91 int main()
 92 {
 93 //    freopen("test","r",stdin);
 94     scanf("%d%d",&n,&bg);{
 95         input();
 96         dfs(0,bg);
 97         total=0;
 98         bfs();
 99         memset(len,0,sizeof(len));
100         init_len(bg);
101         printf("%d\n",total*2-len[bg]);
102     //    flush();
103     }
104     return 0;
105 }

 

  Computer Network

  经典的树形dp,求树中以每个点v为起点的最长路径.

  对每个点v记录两个值

  Len[0][v]:从v点出发的一条最长路径.

  Len[1][v]:从v点出发经过其它儿子的路径中最长的一条.

  然后从根开始dfs:

  当前树的根为u,儿子为v,

  则len[0][v]=max{

    len[0][v];

    len[0][u]+d[u][v];

  }

  如果v在len[0][u]这条路径上,那么要替换成len[1][u].

  mxl[v]更新完后,更新维护len[0][v].

  

Computer network
#include<stdio.h>
#include<string.h>
#define max(a,b) (a)>(b)?(a):(b)
#define maxn 10011
struct edge{
    int v,nxt,d;
};edge e[maxn*2];

struct nd{
    int d,id;
};nd l[2][maxn];

int hd[maxn*2],ne,n,mxl[maxn];
void add_edge(int u,int v,int d)
{
    e[ne].v=v;e[ne].nxt=hd[u];
    e[ne].d=d;hd[u]=ne++;
}
void input()
{
    int i,v,d;
    memset(hd,-1,sizeof(hd));
    for(i=2,ne=0;i<=n;i++){
        scanf("%d%d",&v,&d);
        add_edge(i,v,d);
        add_edge(v,i,d);
    }
}
void dfs1(int fa,int cur)
{
    int i,id=0;
    for(i=hd[cur];i!=-1;i=e[i].nxt)
    if(e[i].v != fa)
        dfs1(cur,e[i].v);

    for(i=hd[cur];i!=-1;i=e[i].nxt)
    if(e[i].v != fa && l[0][cur].d<l[0][e[i].v].d+e[i].d)
        l[0][cur].d=l[0][e[i].v].d+e[i].d,id=e[i].v;
    l[0][cur].id=id;

    for(i=hd[cur];i!=-1;i=e[i].nxt)
    if(e[i].v!=fa && e[i].v!=id && l[1][cur].d<l[0][e[i].v].d+e[i].d)
        l[1][cur].d=l[0][e[i].v].d+e[i].d,l[1][cur].id=e[i].v;
    mxl[cur]=l[0][cur].d;
}
void dfs2(int fa,int cur)
{
    int i,v,j;
    for(i=hd[cur];i!=-1;i=e[i].nxt)
    if(e[i].v!=fa){
        v=e[i].v;

        if(l[0][cur].id == v){

            if(l[0][v].d<l[1][cur].d+e[i].d){
                l[0][v].d=l[1][cur].d+e[i].d,l[0][v].id=cur;
            }

            else if(l[1][v].d<l[1][cur].d+e[i].d)
                l[1][v].d=l[1][cur].d+e[i].d;l[1][v].id=cur;

        }else if(l[0][v].d<l[0][cur].d+e[i].d){
                l[0][v].d=l[0][cur].d+e[i].d,l[0][v].id=cur;
        }
        dfs2(cur,e[i].v);
    }
}
int main()
{
    int i;
    while(scanf("%d",&n)!=EOF){
        input();
        memset(l,0,sizeof(l));
        dfs1(0,1);
        dfs2(0,1);
        for(i=1;i<=n;i++)printf("%d\n",l[0][i].d);
    }
    return 0;
}

  问题可以推展为求树的顶点v所在的最长路径.

 

记录下最近遇到的一些比较麻烦的树形dp统计问题:

  Hourai Jeweled   (2012多校1 by bupt)

题目给出一颗节点带有权值,各边有颜色的树,并定义 '合法路径' 为相邻边颜色不同的路径.

合法路径的权值就是路径中各点权值的累加,求统计所有路径的权值和.

  我用一个含两域的结构题保存了每个节点的信息:

  jew[u].ne: u能传递给它father的路径数.

  jew[u[.val: u传递给他father的路径的权值和

我们dfs的时候要做的事就是利用儿子节点来更新根结点的jew 和 统计各种连接情况下的权值和:

  1. 当前节点u是路径的末端点.

  2.当前节点u是连接它某两个儿子的合法路径中的点;

  3.当前点u是连接它某个儿子和它父亲的合法路径中的点.

1,2可以直接利用儿子节点的jew来统计,3可以转化为它祖先的第一种情况.

Hourai Jeweled
#include<stdio.h>
 #include<string.h>
 #include<vector>
 #include<algorithm>
 #define maxn 300030
 using namespace std;
 typedef long long llong;
 struct nd{
     int v,c;
 };
 struct nd2{
     llong val;
     llong ne;
 };
 vector<nd> e[maxn];
 nd2    jew[maxn];
 int n,val[maxn];
 llong cnt;
 bool cmp(const nd& a,const nd& b){
     return a.c <b.c;
 }
 void input()
 {
     int i,u,v,c;
     nd cur;
     for(i=1;i<=n;i++)scanf("%d",&val[i]);
     for(i=1;i<n;i++){
         scanf("%d%d%d",&u,&v,&c);
         cur.v=v,cur.c=c;
         e[u].push_back(cur);
         cur.v=u,cur.c=c;
         e[v].push_back(cur);
     }
     for(i=1;i<=n;i++)
         sort(e[i].begin(),e[i].end(),cmp);
 }
 void print(int fa,nd cur)
 {
     printf("\n**************\n");
     printf("Father Edge Color:%d\n",cur.c);
     printf("father=%d    son=%d\n",fa,cur.v);
     printf("Edge count=%d val=%d\n",jew[cur.v].ne,jew[cur.v].val);
     printf("\n**************\n");
 }
 void dfs(int col,int fa,int cur)
 {
     int i,l=0;
     llong com=0,comedg=0;
     llong total=0,totedg=0;
     for(i=0;i<e[cur].size();i++)
     if(e[cur][i].v != fa){
         dfs(e[cur][i].c,cur,e[cur][i].v);
         if(e[cur][i].c != col){
         //    print(cur,e[cur][i]);
             jew[cur].ne+=jew[e[cur][i].v].ne;
             jew[cur].val+=jew[e[cur][i].v].val;
         }
         total+=jew[e[cur][i].v].val;
         totedg+=jew[e[cur][i].v].ne;
     }
     cnt+=totedg*val[cur]+total;
     jew[cur].ne++;
     jew[cur].val+=jew[cur].ne*val[cur];
     for(i=0;i<e[cur].size();i++)
     if(e[cur][i].v != fa){
         if(e[cur][i].c != e[cur][l].c){
             cnt+=(totedg-comedg)*com+comedg*(total-com)+comedg*(totedg-comedg)*val[cur];
             total-=com;
             totedg-=comedg;
             com=0;
             comedg=0;
             l=i;
         }
         com+=jew[e[cur][i].v].val;
         comedg+=jew[e[cur][i].v].ne;
     }
 }
 void flush()
 {
     int i;
     for(i=1;i<=n;i++)e[i].clear();
 }
 int main()
 {
 //    freopen("data.in","r",stdin);
     while(scanf("%d",&n)!= EOF)
     {
         input();
         memset(jew,0,sizeof(jew));
         cnt=0;
         dfs(-1,0,1);
         printf("%lld\n",cnt);
         flush();
     }
     return 0;
 }

  Holiday's Accommodatio (2011 Chen'Du Regional Problem H, hdu 4118)

  题目背景是来自n个城市的家庭要访问其它家庭所在的城市,求所有家庭能移动的最大距离和,题目要求不能有两个家庭移动到同一个城市,

并且移动过程中都是走的最短路.

  题目的要求跟最大权匹配类似,但是数据规模巨大,直接跑匹配肯定是行不通的.

  这个问题的统计方法很巧妙,考虑一条边e,设它左边有x个节点,右边有y个节点,那么它最多被通过min(x,y) * 2次,所以用树形dp统计出每个节点为根

的子树的节点数就可以了.

Holiday's Accommodation
#include<stdio.h>
#include<string.h>
#define maxn 100010
#define min(a,b) (a)<(b)?(a):(b)
typedef long long llong;
typedef struct Edge{
    int v;
    int nxt;
    llong w;
}Edge;Edge e[maxn<<1];
int head[maxn<<1],n,cnte,cntp[maxn],pre[maxn],cnts;
bool vis[maxn];
llong ans;
void add_edge(int u,int v,llong w)
{
    e[cnte].v=v;e[cnte].nxt=head[u];
    e[cnte].w=w;head[u]=cnte++;
}
void input()
{
    int i,u,v;
    llong w;
    memset(head,-1,sizeof(head));cnte=0;
    scanf("%d",&n);
    for(i=1;i<n;i++){
        scanf("%d%d%I64d",&u,&v,&w);
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
}
void init_cntp()
{
    int cur=1,i;
    pre[cur]=0;
    memset(vis,0,sizeof(vis));
    memset(cntp,0,sizeof(cntp));
    cntp[cur]=1;
    vis[1]=1;
    while(cur)
    {
        for(i=head[cur];i!=-1;)
        if(!vis[e[i].v]){
            vis[e[i].v]=1;
            pre[e[i].v]=cur;
            cur=e[i].v;
            cntp[cur]=1;
            i=head[cur];
        }else i=e[i].nxt;
        cntp[pre[cur]]+=cntp[cur];
        cur=pre[cur];
    }
}
void statis()
{
    int cur=1,i;
    llong a,b;
    ans=0;
    pre[cur]=0;
    memset(vis,0,sizeof(vis));
    vis[cur]=1;
    while(cur)
    {
        for(i=head[cur];i!=-1;)
        if(!vis[e[i].v]){
            vis[e[i].v]=1;

            a=cntp[cur]-cntp[e[i].v];
            b=cntp[e[i].v];
            ans+=(min(a,b)) * e[i].w*2;

            cntp[e[i].v]=cntp[cur];
            pre[e[i].v]=cur;
            cur=e[i].v;
            i=head[cur];
        }
        else i=e[i].nxt;
        cur=pre[cur];
    }
    printf("%I64d\n",ans);
}
int main()
{
    int cas,t;
//    freopen("test.txt","r",stdin);
    scanf("%d",&cas);
    for(t=1;t<=cas;t++){
        printf("Case #%d: ",t);
        input();
        init_cntp();
        statis();
    }
    return 0;
}

  hdu用dfs会爆栈,写成了非递归的形式.

转载于:https://www.cnblogs.com/eggeek/archive/2012/07/17/2588916.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值