BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治

BZOJ_4016_[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个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

Sample Input

6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1

Sample Output

3 4

首先要理解题中‘字典序最小的含义’。
跑一边dij,对每个点开vector,如果更新前dis[to]==dis[x]+val[i]说明这条反边在最短路图上,就把这条反边加入to的vector中。
于是我们就有了一个最短路图,对每个vector进行排序,然后求出整个图的dfs树就是题中所求的最短路树。
然后就是一个点分治可做的问题了。
维护出当前深度下最长路径和最长路径的条数即可。
 
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
#define N 30050
#define M 60050
#define RR register
inline char nc() {
	static char buf[100000],*p1,*p2;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd() {
	register int x=0;
	register char s=nc();
	while(s<'0'||s>'9') s=nc();
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
	return x;
}
int n,m,vis[N],dis[N],dfn[N],K;
__gnu_pbds::priority_queue<pair<int,int> >q;
vector<int>v[N];
int head[N],to[M<<1],nxt[M<<1],val[M<<1],cnt=1;
__attribute__((optimize("-O2")))void add(int u,int v,int w) {
	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
}
__attribute__((optimize("-O2")))void dij() {
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0; q.push(make_pair(0,1));
	while(!q.empty()) {
		RR int x=q.top().second,i; q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(i=head[x];i;i=nxt[i]) {
			if(dis[to[i]]+val[i]==dis[x]) {
				// puts("FUCK");
				v[to[i]].push_back(i^1);
			}
		}
		for(i=head[x];i;i=nxt[i]) {
			if(dis[to[i]]>dis[x]+val[i]) {
				dis[to[i]]=dis[x]+val[i];
				q.push(make_pair(-dis[to[i]],to[i]));
			}
		}
	}
}
__attribute__((optimize("-O2")))inline bool cmp(int x,int y) {return to[x]<to[y];}
struct Fenzhi {
	int head[N],to[M<<1],nxt[M<<1],val[M<<1],cnt,used[N],sum,ans,tot;
	int f[N],siz[N],root,d[N],dep[N],maxdep,maxmaxdep;
	int g[N],h[N],t1[N],t2[N];//g[i]表示深度为i的点到根路径的最大值,h[i]为对应的方案数。
	__attribute__((optimize("-O2")))void add(int u,int v,int w) {
		to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
	}
	__attribute__((optimize("-O2")))void get_root(int x,int y) {
		RR int i; siz[x]=1; f[x]=0;
		for(i=head[x];i;i=nxt[i]) {
			if(to[i]!=y&&!used[to[i]]) {
				get_root(to[i],x);
				siz[x]+=siz[to[i]];
				f[x]=max(f[x],siz[to[i]]);
			}
		}
		f[x]=max(f[x],sum-siz[x]);
		if(f[x]<f[root]) root=x;
	}
	__attribute__((optimize("-O2")))void get_dep(int x,int y) {
		maxdep=max(maxdep,dep[x]);	
		siz[x]=1;
		RR int i;
		for(i=head[x];i;i=nxt[i]) {
			if(to[i]!=y&&!used[to[i]]) {
				d[to[i]]=d[x]+val[i];
				dep[to[i]]=dep[x]+1;
				if(t1[dep[to[i]]]<d[to[i]]) {
					t1[dep[to[i]]]=d[to[i]]; t2[dep[to[i]]]=1;
				}else if(t1[dep[to[i]]]==d[to[i]]) {
					t2[dep[to[i]]]++;
				}
				get_dep(to[i],x);
				siz[x]+=siz[to[i]];
			}
		}
	}
	__attribute__((optimize("-O2")))void work(int x) {
		maxmaxdep=0;
		used[x]=1;
		RR int i,j;
		for(i=head[x];i;i=nxt[i]) {
			if(!used[to[i]]) {
				maxdep=1;
				d[to[i]]=val[i];
				dep[to[i]]=1; t1[1]=val[i]; t2[1]=1;
				get_dep(to[i],x);
				for(j=1;j<=maxdep&&j<=K;j++) {
					if(ans<t1[j]+g[K-j]) ans=t1[j]+g[K-j],tot=t2[j]*h[K-j];
					else if(ans==t1[j]+g[K-j]) tot+=t2[j]*h[K-j];
				}
				for(j=1;j<=maxdep&&j<=K;j++) {
					if(t1[j]>g[j]) g[j]=t1[j],h[j]=t2[j];
					else if(t1[j]==g[j]) h[j]+=t2[j];
				}
				for(j=1;j<=maxdep;j++) t1[j]=-1<<30,t2[j]=0;
				maxmaxdep=max(maxmaxdep,maxdep);
			}
		}

		for(i=1;i<=maxmaxdep&&i<=K;i++) g[i]=-1<<30,h[i]=0;
		for(i=head[x];i;i=nxt[i]) {
			if(!used[to[i]]) {
				root=0;
				sum=siz[to[i]];
				get_root(to[i],x);
				work(root);
			}
		}
	}
	__attribute__((optimize("-O2")))void solve() {
		sum=n; root=0; f[0]=1<<30;
		get_root(1,0); work(root);
		printf("%d %d
",ans,tot);
	}
}F;
__attribute__((optimize("-O2")))void dfs(int x) {
	RR int i;
	dfn[x]=1;
	sort(v[x].begin(),v[x].end(),cmp);
	for(i=0;i<(int)v[x].size();i++) {
		int t=v[x][i],c=val[t],v=to[t];
		if(!dfn[v]) {
			dfn[v]=1; F.add(x,v,c); F.add(v,x,c); dfs(v);
		}
	}
}
__attribute__((optimize("-O2")))int main() {
	n=rd(); m=rd(); K=rd(); K--;
	RR int i,x,y,z;
	for(i=1;i<=m;i++) {
		x=rd(); y=rd(); z=rd();
		add(x,y,z); add(y,x,z);
	}
	dij();
	dfs(1);
	F.h[0]=1;
	F.solve();
}
原文地址:https://www.cnblogs.com/suika/p/9062416.html