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

来源:互联网 发布:网络拓扑环形结构 编辑:程序博客网 时间:2024/06/05 11:18
/*题意:给你一些节点和一些边,求最短路径树上是k个节点的最长的路径数。解:1、求出最短路径树--spfa加记录    2、树上进行操作--树的分治,分别处理子树进行补集等运算*/#include<stdio.h>#include<string.h>#include<stdlib.h>#include<algorithm>#include<iostream>#include<queue>#define ll __int64using namespace std;#define N  31000#define inf 10000000000000000ll 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;}

0 0