[HDU6074] Phone Call

来源:互联网 发布:关机准备配置windows 编辑:程序博客网 时间:2024/06/05 17:39

Problem Link

Description

    给定一棵有n个节点的树,还有m个点集,各个点集里的点可以互相传递消息,并且每次传递消息的花费为w,描述每个点集我们给定abcdw,表明这个点集是由ab的路径上的点并上cd的路径上的点构成的,各个点之间传递消息的花费为w。一开始只有1能传递消息,被传到消息的点也能传递消息。求出最多有多少个点能被传到消息以及达到这个要求的最小花费(答案包括1这个点)。

Solution

    很容易看出来这是一道类似于最小生成树的题目,我们只要将集合按照w从小到大排序,然后遍历一遍就行了。对于一个区间,我们先让ab路径上的点并到LCA中,再让cd路径上的点并到LCA中,然后再把两个LCA并在一起。跑一条路径时我们不能一个一个往上跳,而是直接跳到深度最深的和自己不在同一个连通块的点,这样就最多只用跑n个点了。注意:每次合并时我们把fa深度大的点并到fa深度小的点上,这样可以保证最后并到的一定是1。

Code

#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>#define M 100005#define ll long longusing namespace std;template <class T>inline void Rd(T &res){    char c;res=0;int k=1;    while(c=getchar(),c<48&&c!='-');    if(c=='-'){k=-1;c='0';}    do{        res=(res<<3)+(res<<1)+(c^48);    }while(c=getchar(),c>=48);    res*=k;}template <class T>inline void Pt(T res){    if(res<0){        putchar('-');        res=-res;    }    if(res>=10)Pt(res/10);    putchar(res%10+48);}struct edge{    int v,nxt;}e[M<<1];struct node{    int a,b,c,d,w;    bool operator <(const node &tmp)const{        return w<tmp.w;    }}s[M];void swap(int &a,int &b){    int t=a;a=b;b=t;}int T;int n,m;int fa[M],sum[M];ll cost[M];int pa[M],dep[M],top[M],son[M],sz[M];int head[M],nxt[M],cnt;void add_edge(int u,int v){    e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;}void dfs(int x,int t){    nxt[x]=pa[x]=t;    sz[x]=1;    if(~t)dep[x]=dep[t]+1;    for(int i=head[x];~i;i=e[i].nxt){        int v=e[i].v;        if(v==t)continue;        dfs(v,x);        sz[x]+=sz[v];        if(sz[son[x]]<sz[v])son[x]=v;    }}void rdfs(int x,int t,int tp){    top[x]=tp;    for(int i=head[x];~i;i=e[i].nxt){        int v=e[i].v;        if(v==t)continue;        if(v==son[x])rdfs(v,x,tp);        else rdfs(v,x,v);    }}int LCA(int u,int v){    while(top[u]!=top[v]){        if(dep[top[u]]>dep[top[v]])u=pa[top[u]];        else v=pa[top[v]];    }    return dep[u]<dep[v]?u:v;}int getfa(int v){    if(fa[v]==v)return v;    return fa[v]=getfa(fa[v]);}bool same(int x,int y){    return getfa(x)==getfa(y);}void merge(int x,int y,int w){//把dep大的并到dep小的上     int px=getfa(x);    int py=getfa(y);    if(dep[px]>dep[py])swap(px,py);    sum[px]+=sum[py];    fa[py]=px;    cost[px]+=cost[py]+w;}void calc(int u,int v,int w,int lca){    while(~u&&dep[u]>dep[lca]){        if(!same(u,lca))merge(lca,u,w);        int tmp=nxt[u];        nxt[u]=pa[lca];        u=tmp;    }    while(~v&&dep[v]>dep[lca]){        if(!same(v,lca))merge(lca,v,w);        int tmp=nxt[v];        nxt[v]=pa[lca];        v=tmp;    }}void solve(int a,int b,int c,int d,int w){    int x=LCA(a,b),y=LCA(c,d);    calc(a,b,w,x);    calc(c,d,w,y);    if(!same(x,y))merge(x,y,w);}int main(){    memset(pa,-1,sizeof(pa));    memset(nxt,-1,sizeof(nxt));    int a,b;    Rd(T);    while(T--){        memset(head,-1,sizeof(head));        memset(son,0,sizeof(son));        memset(cost,0,sizeof(cost));        cnt=0;        Rd(n);Rd(m);        for(int i=1;i<n;i++){            fa[i]=i;            sum[i]=1;            Rd(a);Rd(b);            add_edge(a,b);            add_edge(b,a);        }        fa[n]=n;sum[n]=1;        for(int i=1;i<=m;i++){            Rd(s[i].a);Rd(s[i].b);Rd(s[i].c);Rd(s[i].d);Rd(s[i].w);        }        sort(s+1,s+m+1);        dfs(1,-1);        rdfs(1,-1,1);        for(int i=1;i<=m;i++)        solve(s[i].a,s[i].b,s[i].c,s[i].d,s[i].w);        Pt(sum[1]);        putchar(' ');        Pt(cost[1]);        putchar('\n');    }    return 0;}

    博主写得比较臭,没进行任何优化,所以很慢,还请读者自己写写看,还是蛮容易的。

原创粉丝点击