4016: [FJOI2014]最短路径树问题

4016: [FJOI2014]最短路径树问题

Time Limit: 5 Sec   Memory Limit: 512 MB
Submit: 411   Solved: 136
[ Submit][ Status][ Discuss]

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

HINT

对于所有数据n<=30000,m<=60000,2<=K<=n。数据保证最短路径树上至少存在一条长度为K的路径。


Source

[ Submit][ Status][ Discuss]

SPFA+树的点分治 苟蒻花了。。。好像很长时间(果然还是蠢) 对于点分治不再叙述,关于SPFA找最短路: 先跑一遍spfa求出从起点出发到各个点的最短距离,然后新图只留dis[i] + w == dis[j]这样的边,将他们按编号从小到大排序,最后一个dfs解决
关于犯的傻逼错误: 没考虑深度相等时的情况 数组嵌套的时候漏数组a[b[c[]]]   全局变量与局部变量使用出事 受思维定式影响代码编写准确性 点分治应该将所有被选作重心的点标号

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

const int maxn = 3E4 + 30;

struct E{
	int to,w;
};

vector <E> v1[maxn],v2[maxn];
vector <int> v3[maxn*20];
queue <int> q;

int dis[maxn],n,m,k,vis[maxn],cnt,L[maxn];
int ans1,ans2,o,Max,maxl,T[maxn],disl[maxn],father[maxn];
int siz[maxn],M,mark[maxn],head[maxn],tail[maxn],cur,cost[maxn];
bool set[maxn],USE[maxn];

void DFS(int x)
{
	for (int l = 0; l < v2[x].size(); l++) {
		int to = v2[x][l].to;
		if (vis[to] != cnt) {
			vis[to] = cnt; father[to] = x; cost[to] = v2[x][l].w;
			v1[to].push_back((E){x,v2[x][l].w});
			v1[x].push_back(v2[x][l]);
			DFS(to);
		}
	}
}

void cal(int x)
{
	if (L[x] == 1) M = x;
	mark[x] = M;
	for (int i = 0; i < v1[x].size(); i++) {
		int to = v1[x][i].to;
		if (USE[to] || vis[to] == cnt) continue;
		int w = v1[x][i].w;
		vis[to] = cnt;
		dis[to] = dis[x] + w;
		L[to] = L[x] + 1;
		maxl = max(maxl,L[to]);
		cal(to);
	}
}

void DFS2(int x)
{
	siz[x] = 1; v3[cur].push_back(x);
	for (int i = 0; i < v1[x].size(); i++) {
		int to = v1[x][i].to;
		if (vis[to] == cnt || USE[to]) continue;
		vis[to] = cnt; father[to] = x;
		DFS2(to);
		siz[x] += siz[to];
	}
}

void search(int root)
{
	Max = ~0U>>1;
	for (int l = 0; l < v3[cur].size(); l++) {
		int x = v3[cur][l]; 
		int now = siz[root] - siz[x];
		for (int i = 0; i < v1[x].size(); i++) {
			int to = v1[x][i].to;
			if (to == father[x] || USE[to]) continue;
			now = max(now,siz[to]);
		}
		if (now < Max) Max = now,o = x;
	}
}

bool cmp2(const int &a,const int &b)
{
	if (L[a] < L[b]) return 1;
	if (L[a] > L[b]) return 0;
	return mark[a] < mark[b];
}

void Work()
{
	head[0] = 0; tail[maxl] = v3[cur].size()-1;
	for (int i = 0; i < tail[maxl]; i++) {
		int A = v3[cur][i];
		int B = v3[cur][i+1];
		if (L[A] != L[B]) {
			tail[L[A]] = i;
			head[L[B]] = i+1;
		}
	}
	for (int i = 0; i <= maxl; i++) {
		int j = k-i-1;
		if (j < i) break;
		if (j > maxl) continue;
		for (int l = head[i]; l <= tail[i]; l++) {
			int A = v3[cur][l];
			disl[mark[A]] = T[mark[A]] = set[mark[A]] = 0;
		}	
		for (int l = head[j]; l <= tail[j]; l++) {
			int A = v3[cur][l];
			disl[mark[A]] = T[mark[A]] = set[mark[A]] = 0;
		}	
			
		int ma,ti; ma = ti = 0;
		for (int l = head[j]; l <= tail[j]; l++) {
			int A = v3[cur][l];
			if (dis[A] == ma) ++ti;
			if (dis[A] > ma) {
				ma = dis[A]; ti = 1;
			}
			set[mark[A]] = 1;
			if (l < tail[j] && mark[A] != mark[v3[cur][l+1]]) {
				int B = v3[cur][l+1];
				disl[mark[B]] = ma;
				T[mark[B]] = ti;
			}
		}
		int ma2,ti2; ma2 = ti2 = 0;
		for (int l = tail[j]; l >= head[j]; l--) {
			int A = v3[cur][l];
			if (dis[A] == ma2) ++ti2;
			if (dis[A] > ma2) {
				ma2 = dis[A]; ti2 = 1;
			}
			set[mark[A]] = 1;
			if (l > head[j] && mark[A] != mark[v3[cur][l-1]]) {
				int B = v3[cur][l-1];
				if (ma2 > disl[mark[B]]) {
					disl[mark[B]] = ma2; 
					T[mark[B]] = ti2;
				}
				else if (ma2 == disl[mark[B]]) T[mark[B]] += ti2;
			}
		}
		if (ma2 > ma) ma = ma2,ti = ti2;
		
		int A1,A2; A1 = A2 = 0;
		for (int l = head[i]; l <= tail[i]; l++) {
			int now = v3[cur][l];
			int mar = mark[now];
			int MAX,TIME;
			if (!disl[mar] && !set[mar]) MAX = ma,TIME = ti;
			else MAX = disl[mar],TIME = T[mar];
			if (dis[now] + MAX == A1) A2 += TIME;
			else if (dis[now] + MAX > A1) {
				A1 = dis[now] + MAX;
				A2 = TIME;
			}
		}
		if (i == j) A2 /= 2;
		if (A1 == ans1) ans2 += A2;
		if (A1 > ans1) ans1 = A1,ans2 = A2;
	}
	
}

void solve(int root)
{
	++cnt; 
	vis[root] = cnt;
	DFS2(root);
	search(root);
	
	L[o] = dis[o] = maxl = 0;
	USE[o] = 1;
	++cnt; vis[o] = cnt; father[o] = 0;
	cal(o);
	sort(v3[cur].begin(),v3[cur].end(),cmp2);
	mark[v3[cur][0]] = 0;
	
	/*for (int i = 0; i < v3[cur].size(); i++) {
		int x = v3[cur][i];
		int ll = L[x]; 
		int mm = mark[x];
	}*/
	
	Work();
	
	if (maxl*2-1 < k) return;
	int O = o;
	for (int i = 0; i < v1[O].size(); i++) {
		int to = v1[O][i].to;
		if (USE[to]) continue;
		++cur;
		solve(to);
	}
}

bool cmp(const E &a,const E &b)
{
	return a.to < b.to;
}

int getint()
{
	int ret = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while ('0' <= ch && ch <= '9') {
		ret = ret*10 + ch - '0';
		ch = getchar();
	}
	return ret;
}

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n >> m >> k;
	
	for (int i = 1; i <= m; i++) {
		int x,y,z;
		x = getint(); y = getint(); z = getint();
		v1[x].push_back((E){y,z});
		v1[y].push_back((E){x,z});
	}
	memset(dis,127,sizeof(dis));
	dis[1] = 0;
	memset(vis,0,sizeof(vis)); ++cnt;
	vis[1] = cnt;
	q.push(1);
	while (!q.empty()) {
		int K = q.front(); 
		q.pop(); vis[K] = 0;
		for (int l = 0; l < v1[K].size(); l++) {
			int to = v1[K][l].to;
			int w = v1[K][l].w;
			if (dis[to] > dis[K] + w) {
				dis[to] = dis[K] + w;
				if (!vis[to]) {
					vis[to] = cnt;
					q.push(to);
				}
			}	
		}
	}
	
	for (int i = 1; i <= n; i++) 
		for (int j = 0; j < v1[i].size(); j++) {
			int to = v1[i][j].to;
			int w = v1[i][j].w;
			if (dis[to] == dis[i] + w) v2[i].push_back(v1[i][j]);
		} 
	for (int i = 1; i <= n; i++) sort(v2[i].begin(),v2[i].end(),cmp);
	memset(vis,0,sizeof(vis));
	for (int i = 1; i <= n; i++) v1[i].clear();
	++cnt; vis[1] = cnt;
	DFS(1); 
	
	for (int i = 1; i <= n; i++) {
		int aaa = 1;
	}
		
	ans1 = ans2 = 0;
	memset(USE,0,sizeof(USE));
	solve(1);
	printf("%d %d",ans1,ans2);
	
	return 0;
}


你可能感兴趣的:(4016: [FJOI2014]最短路径树问题)