码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 4871 树的分治+最短路记录路径

时间:2014-10-04 19:31:27      阅读:266      评论:0      收藏:0      [点我收藏+]

标签:io   os   for   sp   c   on   amp   r   ad   

/*
题意:给你一些节点和一些边,求最短路径树上是k个节点的最长的路径数。
解:1、求出最短路径树--spfa加记录
    2、树上进行操作--树的分治,分别处理子树进行补集等运算
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<queue>
#define ll __int64
using namespace std;
#define N  31000
#define inf 10000000000000000
ll kk;
struct node {
ll u,v,w,next;
}bian[N*4];
ll yong,head[N];
void init() {
yong=0;
memset(head,-1,sizeof(head));
}
void addedge(ll u,ll v,ll w) {
bian[yong].u=u;
bian[yong].v=v;
bian[yong].w=w;
bian[yong].next=head[u];
head[u]=yong++;
}
ll Min(ll v,ll vv) {
return v>vv?vv:v;
}
ll premi[N],val[N];//用来记录前一个元素的字典序最小和前一条边的权值
void spfa(ll u,ll n) {
  ll i,cur,dis[N],vis[N];
  queue<ll>q;
  for(i=1;i<=n;i++)
    dis[i]=inf;
    memset(vis,0,sizeof(vis));
    memset(premi,-1,sizeof(premi));
  dis[u]=0;
  q.push(u);
  while(!q.empty()) {
    cur=q.front();
    q.pop();
    vis[cur]=0;
    for(i=head[cur];i!=-1;i=bian[i].next) {
        ll v=bian[i].v;
        if(dis[v]>dis[cur]+bian[i].w) {
            dis[v]=dis[cur]+bian[i].w;
            val[v]=bian[i].w;
            premi[v]=cur;//记录前一个节点
            if(!vis[v]) {
                vis[v]=1;
                q.push(v);
            }
        }
        else
        if(dis[v]==dis[cur]+bian[i].w) {
            if(premi[v]==-1)premi[v]=cur;
            else
                premi[v]=Min(premi[v],cur);
        }
    }
  }

  return ;
}
/*以下是树的分治部分*/
ll minn,ma,num[N],nn,diss[N],len,mxx,mxnum,vis[N],ed[N];
void dfs1(ll u,ll fa) {
ll i;
nn++;
for(i=head[u];i!=-1;i=bian[i].next) {
    ll v=bian[i].v;
    if(v!=fa&&!vis[v])
        dfs1(v,u);
}
return ;
}
ll Max(ll v,ll vv) {
return v>vv?v:vv;
}
void dfs2(ll u,ll fa) {
num[u]=1;
ll i,tit=0;
for(i=head[u];i!=-1;i=bian[i].next) {
    ll v=bian[i].v;
    if(v!=fa&&!vis[v]) {
        dfs2(v,u);
        num[u]+=num[v];
        tit=Max(tit,num[v]);
    }
}
tit=Max(tit,nn-num[u]);
if(tit<minn) {
    minn=tit;
    ma=u;
}
return;
}
void dfs4(ll u,ll fa,ll w,ll aa) {
  diss[++len]=w;
  ed[len]=aa;
  ll i;
  for(i=head[u];i!=-1;i=bian[i].next) {
    ll v=bian[i].v;
    if(v!=fa&&!vis[v])
        dfs4(v,u,w+bian[i].w,aa+1);
  }
  return;
}
struct nodee {//用来记录补集
 ll dis;
 ll num;
}f[N];
void dfs3(ll u) {
   ll i,k,j;
   if(nn<kk)return ;
    for(i=0;i<=nn;i++)
        f[i].dis=0,f[i].num=0;
   for(i=head[u];i!=-1;i=bian[i].next) {
    ll v=bian[i].v;
    if(vis[v])continue;
    len=0;
  //  printf("v=%I64d w=%I64d\n",v,bian[i].w);
    dfs4(v,-1,bian[i].w,1);//因为这里实际是两个节点但是要把它看成一个节点
  //  printf("len=%I64d\n",len);
    for(j=1;j<=len;j++) {
        //    printf("z%I64d %I64d %I64d\n",j,ed[j],diss[j]);
    if(ed[j]+1==kk) {//和当前子树比较
            if(diss[j]>mxx) {
                    mxx=diss[j];
                    mxnum=1;
                }
                else
                    if(diss[j]==mxx)
                    mxnum++;
            }
            if(kk-ed[j]-1<=0)continue;
        k=diss[j]+f[kk-ed[j]].dis;//补集
      //  printf("khe=%I64d\n",k);
        if(k>mxx) {
            mxx=k;
           mxnum=f[kk-ed[j]].num;
        }
        else
        if(k==mxx)
         mxnum+=f[kk-ed[j]].num;
    }
    for(j=1;j<=len;j++) {//加入补集
        if(ed[j]+1>=kk)continue;
          if(f[ed[j]+1].dis<diss[j]) {//节点数要加一加入
            f[ed[j]+1].dis=diss[j];
            f[ed[j]+1].num=1;
          }
          else if(f[ed[j]+1].dis==diss[j])
            f[ed[j]+1].num++;
    }
   }
   //printf("%I64d %I64d\n",mxx,mxnum);
   return ;
}
void dfs(ll u) {
  minn=inf;
  nn=0;
  dfs1(u,-1);
  dfs2(u,-1);
  //printf("minn=%I64d %I64d\n",minn,ma);
  vis[ma]=1;
  dfs3(ma);
  ll i;
  for(i=head[ma];i!=-1;i=bian[i].next) {
    ll v=bian[i].v;
    if(!vis[v])
        dfs(v);
  }
  return;
}
int main() {
   ll t,n,m,i,j,k;
   scanf("%I64d",&t);
   while(t--) {
    init();
    scanf("%I64d%I64d%I64d",&n,&m,&kk);
    while(m--) {
        scanf("%I64d%I64d%I64d",&i,&j,&k);
        addedge(i,j,k);
        addedge(j,i,k);
    }
    if(kk==1) {//是1的时候特殊处理
        printf("0 %I64d\n",n);
        continue;
    }
    spfa(1,n);//求最短路
    //prllf("z");
    init();
    for(i=2;i<=n;i++) {//建立最短路径树
        addedge(i,premi[i],val[i]);
        addedge(premi[i],i,val[i]);
    }
  // for(i=0;i<yong;i++)
  //      printf("%I64d %I64d %I64d\n",bian[i].u,bian[i].v,bian[i].w);
    memset(vis,0,sizeof(vis));
    mxx=-1;mxnum=0;//用来记录最长值和路径数
    dfs(1);
    printf("%I64d %I64d\n",mxx,mxnum);
   }
return 0;}

hdu 4871 树的分治+最短路记录路径

标签:io   os   for   sp   c   on   amp   r   ad   

原文地址:http://blog.csdn.net/u011483306/article/details/39780769

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!