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

/*
题意:给你一些节点和一些边,求最短路径树上是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;}

时间: 2024-08-25 17:10:37

hdu 4871 树的分治+最短路记录路径的相关文章

uva 11374 最短路+记录路径 dijkstra最短路模板

UVA - 11374 Airport Express Time Limit:1000MS   Memory Limit:Unknown   64bit IO Format:%lld & %llu [Submit]  [Go Back]  [Status] Description ProblemD: Airport Express In a small city called Iokh, a train service, Airport-Express, takes residents to t

HDOJ 5294 Tricks Device 最短路(记录路径)+最小割

最短路记录路径,同时求出最短的路径上最少要有多少条边, 然后用在最短路上的边重新构图后求最小割. Tricks Device Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1584    Accepted Submission(s): 388 Problem Description Innocent Wu follows Dumb Z

最短路 + 记录路径 之 zoj 1456 Minimum Transport Cost (hdu 1385)

/* 考虑到测试数据中需要求解任意两点间的最短路,所以采用Floyd-Warshall算法 dp[i][j] = min(dp[i][k] + dp[k][j] + tax[k], dp[i][j]); 关键在于记录路径,并且要保证:if there are more minimal paths, output the lexically smallest one. 分两种情况讨论: (1)dp[i][j] > dp[i][k] + dp[k][j] + tax[k] 直接更新dp[i][j],

HDU1026--Ignatius and the Princess I(BFS记录路径)

Problem Description The Princess has been abducted by the BEelzebub feng5166, our hero Ignatius has to rescue our pretty Princess. Now he gets into feng5166's castle. The castle is a large labyrinth. To make the problem simply, we assume the labyrint

HDU 1540(线段树+区间合并)学习记录

学习了线段树的新姿势,记录一下 参考blog:https://blog.csdn.net/sunyutian1998/article/details/79618316 query的时候m-ql+1和qr-m写成了m-l+1.r-m,wa了几发之后才找到bug 错误样例: 10 1Q 5 wrong answer:5 顺带一提,这个题目可以在set上面二分直接过,不过最主要还是为了练习区间合并 #include<bits/stdc++.h> using namespace std; const

HDU 1754 树状数组 解法

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

hdu 4897 树链剖分(重轻链)

Little Devil I Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 894    Accepted Submission(s): 296 Problem Description There is an old country and the king fell in love with a devil. The devil

hdu 1690 Bus System(Dijkstra最短路)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1690 Bus System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6569    Accepted Submission(s): 1692 Problem Description Because of the huge popula

HDU 4911 (树状数组+逆序数)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4911 题目大意:最多可以交换K次,就最小逆序对数 解题思路: 逆序数定理,当逆序对数大于0时,若ak<ak+1,那么交换后逆序对数+1,反之-1. 设原始序列最小逆序对数=cnt 那么,交换K次的最小逆序对数max(0,cnt-k) 在求原始序列最小逆序对数上,朴素暴力复杂度O(n^2)不可取 有以下两种O(nlogn)的方法: ①排序内计算: 主要是利用归并排序内的特性,即相邻两个归并序列逆序情