【BZOJ4016】【FJOI2014】最短路径树问题

题意:

Description

给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

Input

第一行输入三个正整数n,m,K,表示有n个点m条边,要求的路径需要经过K个点。接下来输入m行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi间有一条长度为Ci的边。数据保证输入的是连通的无向图。

Output

输出一行两个整数,以一个空格隔开,第一个整数表示包含K个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

n<=30000,m<=60000,2<=K<=n

题解:

一眼题啊。。。SBFA写挂能怪谁QAQ

先把最小路径树建出来,然后就是点分治经典问题了;

关于最小路径树:

先以1为源点跑一边最短路,然后把对最短路没有贡献的边去掉,再按照字典序dfs一次把非树边去掉(有可能有多个最短路)即可。

代码:

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<queue>
  7 #define inf 2147483647
  8 #define eps 1e-9
  9 using namespace std;
 10 typedef long long ll;
 11 struct edge{
 12     int v,w,next; 
 13 }a[200001],_a[200001],__a[200001];
 14 struct _edge{
 15     int u,v,w;
 16     friend bool operator <(_edge a,_edge b){
 17         return a.u==b.u?a.v>b.v:a.u<b.u;
 18     }
 19 }e[200001];
 20 int n,m,k,u,v,w,ans=0,anss,rt,S,tot=0,_tot=0,__tot=0,siz[200001],mx[200001],head[200001],_head[200001],__head[200001],f[200001],g[200001],_f[200001],_g[200001],dis[200001];
 21 bool used[200001],vis[200001];
 22 void add(int u,int v,int w){
 23     a[++tot].v=v;
 24     a[tot].w=w;
 25     a[tot].next=head[u];
 26     head[u]=tot;
 27 }
 28 void _add(int u,int v,int w){
 29     _a[++_tot].v=v;
 30     _a[_tot].w=w;
 31     _a[_tot].next=_head[u];
 32     _head[u]=_tot;
 33 }
 34 void __add(int u,int v,int w){
 35     __a[++__tot].v=v;
 36     __a[__tot].w=w;
 37     __a[__tot].next=__head[u];
 38     __head[u]=__tot;
 39 }
 40 void spfa(){
 41     queue<int>q;
 42     bool isin[200001];
 43     memset(isin,0,sizeof(isin));
 44     q.push(1);
 45     isin[1]=true;
 46     dis[1]=0;
 47     while(!q.empty()){
 48         int u=q.front();
 49         q.pop();
 50         isin[u]=false;
 51         for(int tmp=_head[u];tmp!=-1;tmp=_a[tmp].next){
 52             int v=_a[tmp].v;
 53             if(dis[v]>dis[u]+_a[tmp].w){
 54                 dis[v]=dis[u]+_a[tmp].w;
 55                 if(!isin[v]){
 56                     isin[v]=true;
 57                     q.push(v);
 58                 }
 59             }
 60         }
 61     }
 62     sort(e+1,e+m*2+1);
 63     for(int i=1;i<=m*2;i++){
 64         if(dis[e[i].v]==dis[e[i].u]+e[i].w){
 65             __add(e[i].u,e[i].v,e[i].w);
 66         }
 67     }
 68 }
 69 void build(int u){
 70     used[u]=true;
 71     for(int tmp=__head[u];tmp!=-1;tmp=__a[tmp].next){
 72         int v=__a[tmp].v,w=__a[tmp].w;
 73         if(!used[v]){
 74             //printf("%d %d %d\n",u,v,w);
 75             add(u,v,w);
 76             add(v,u,w);
 77             build(v);
 78         }
 79     }
 80 }
 81 void dfsrt(int u,int fa){
 82     siz[u]=1;
 83     mx[u]=0;
 84     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 85         int v=a[tmp].v;
 86         if(v!=fa&&!vis[v]){
 87             dfsrt(v,u);
 88             mx[u]=max(mx[u],siz[v]);
 89             siz[u]+=siz[v];
 90         }
 91     }
 92     mx[u]=max(mx[u],S-siz[u]);
 93     if(mx[u]<mx[rt])rt=u;
 94 }
 95 void dfs(int u,int fa,int dpt,int ww){
 96     if(dpt>k)return;
 97     if(ww>_f[dpt]){
 98         _f[dpt]=ww;
 99         _g[dpt]=1;
100     }else if(ww==_f[dpt]){
101         _g[dpt]++;
102     }
103     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
104         int v=a[tmp].v;
105         if(v!=fa&&!vis[v]){
106             dfs(v,u,dpt+1,ww+a[tmp].w);
107         }
108     }
109 }
110 void divide(int u){
111     f[0]=0,g[0]=1;
112     for(int i=1;i<=k;i++)f[i]=g[i]=0;
113     vis[u]=true;
114     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
115         int v=a[tmp].v,w=a[tmp].w;
116         if(!vis[v]){
117             for(int i=0;i<=k;i++)_f[i]=_g[i]=0;
118             dfs(v,0,1,w);
119             for(int i=1;i<=k;i++){
120                 if(ans<f[k-i]+_f[i]){
121                     ans=f[k-i]+_f[i];
122                     anss=g[k-i]*_g[i];
123                 }else if(ans==f[k-i]+_f[i]){
124                     anss+=g[k-i]*_g[i];
125                 }
126             }
127             for(int i=1;i<=k;i++){
128                 if(f[i]<_f[i]){
129                     f[i]=_f[i];
130                     g[i]=_g[i];
131                 }else if(f[i]==_f[i]){
132                     g[i]+=_g[i];
133                 }
134             }
135         }
136     }
137     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
138         int v=a[tmp].v;
139         if(!vis[v]){
140             rt=0,S=siz[v];
141             dfsrt(v,0);
142             divide(rt);
143         }
144     }
145 }
146 int main(){
147     memset(dis,0x3f,sizeof(dis));
148     memset(head,-1,sizeof(head));
149     memset(_head,-1,sizeof(_head));
150     memset(__head,-1,sizeof(__head));
151     memset(used,0,sizeof(used));
152     memset(vis,0,sizeof(vis));
153     scanf("%d%d%d",&n,&m,&k);
154     k--;
155     for(int i=1;i<=m;i++){
156         scanf("%d%d%d",&u,&v,&w);
157         _add(u,v,w);
158         _add(v,u,w);
159         e[i*2-1]=(_edge){u,v,w};
160         e[i*2]=(_edge){v,u,w};
161     }
162     spfa();
163     build(1);
164     mx[rt=0]=666666,S=n;
165     dfsrt(1,0);
166     divide(rt);
167     printf("%d %d",ans,anss);
168     return 0;
169 }

PS:BZOJ数据极水,我写了SPFA+没判字典序都过了

posted @ 2018-09-29 19:22  DCDCBigBig  阅读(382)  评论(1编辑  收藏  举报