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個點的路徑最長爲多長,第二個整數表示這樣的不同的最長路徑有多少條。


題目大意大概就是要根據規定的一些條件建樹,然後要求出2個問題,注意第二問長度爲該長度的路徑也必須包含K個點。

建圖的話,首先跑一遍最短路,然後對於一個點u,從小到大枚舉與其相鄰每個點,若是該點在最短路圖上,則建邊樹。

然後進行樹分治,首先我們開一個數組a[i]保存經過i條邊的路徑的最大長度,用b[i][j]表示經過i條邊路徑長度爲j的路徑條數,然後對於一個點的每棵子樹單獨處理,現在我們考慮合併的情況,假設我們已知前i-1棵子樹的信息,然後我們搜索第i棵子樹,用dis[]表示經過的邊數,用dist表示經過的路徑長度,若是存在a[K - dis[u] - 1],則判斷大小,更新,在保證路徑長度最長的情況下更新邊數。由於map數組b速度非常慢(在windows下最慢的點跑了1.40s),我們可以用一個權值線段樹來優化,時間複雜度O(n log ^ 2(n)).

(當然我的做法是偏複雜的,存在O(n log n)的做法,請讀者自行思考)

附上代碼:

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXX = 60005;
const int INF = 300000005;
const int MAXN = 6000005;
int first[MAXX], next[MAXX << 1], go[MAXX << 1], way[MAXX << 1], t, ans, ans1;
int first1[MAXX], next1[MAXX << 1], go1[MAXX << 1], way1[MAXX << 1], a[MAXX];
int n, i, j, k, l, m, K, root, sroot, size[MAXX];
int dis[MAXX], q[MAXX << 5], w, dist[MAXX], tot;
int sum[MAXN], lc[MAXN], rc[MAXN], len, rt[MAXX];
bool e[MAXX], vis[MAXX];
struct sb{
	int x, y, z;
};
sb sta[MAXX << 1];
inline bool rule(const sb &a, const sb &b)
{
	return (a.x < b.x || (a.x == b.x && a.y > b.y));
}
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
	res = res * 10 + c - 48;
	return res;
}
inline void add1(const int &x, const int &y, const int &z)
{
	next1[++t] = first1[x]; first1[x] = t; go1[t] = y; way1[t] = z;
}
inline void add(const int &x, const int &y, const int &z)
{
	next[++t] = first[x]; first[x] = t; go[t] = y; way[t] = z;
	next[++t] = first[y]; first[y] = t; go[t] = x; way[t] = z;
}
inline int MAX(const int &x, const int &y)
{
	if (x > y) return x;
	else return y;
}
inline void bfs()
{
	for(int i = 1; i <= n; i ++)
		dis[i] = 707406378;
	dis[q[e[w = 1] = 1] = 1] = 0;
	for(int lpf = 1; lpf <= w; lpf ++)
	{
		int k = q[lpf];
		for(int i = first1[k]; i; i = next1[i])
		if (dis[go1[i]] > dis[k] + way1[i])
		{
			dis[go1[i]] = dis[k] + way1[i];
			if (!e[go1[i]])
			{
				e[go1[i]] = 1;
				q[++w] = go1[i];
				if (dis[q[w]] < dis[q[lpf + 1]]) swap(q[w], q[lpf + 1]);
			}
		}
		e[k] = 0;
	}
}
inline void dfs1(const int &now)
{
	vis[now] = 1;
	for(int i = first1[now]; i; i = next1[i])
	{
		int u = go1[i], v = way1[i];
		if (dis[u] == dis[now] + v && !vis[u])
		{
			add(now, u, v);
			dfs1(u);
		}
	}
}
inline void insert(int &k, const int &p, const int &q, const int &w, int ww)
{
	if (!k) k = ++len;
	if (p == q && p == w)
	{
		sum[k] += ww;
		return;
	}
	int mid = (p + q) >> 1;
	if (mid >= w) insert(lc[k], p, mid, w, ww);
	if (mid < w) insert(rc[k], mid + 1, q, w, ww);
}
inline void find(const int &k, const int &p, const int &q, const int &w)
{
	if (p == q && p == w)
	{
		tot = sum[k];
		return;
	}
	int mid = (p + q) >> 1;
	if (mid >= w) find(lc[k], p, mid, w);
	if (mid < w) find(rc[k], mid + 1, q, w);
}
inline void getroot(const int &now, const int &fa, int p)
{
	size[now] = 1;
	int num = 0;
	for(int i = first[now]; i; i = next[i])
		if (go[i] != fa && !vis[go[i]]) getroot(go[i], now, p), size[now] += size[go[i]], num = MAX(num, size[go[i]]);
	if (p - size[now] > num) num = p - size[now];
	if (num < sroot) sroot = num, root = now;
}
inline void getdis(const int &now, const int &fa)
{
	if (dis[now] >= K) return;
	a[dis[now]] = MAX(a[dis[now]], dist[now]);
	insert(rt[dis[now]], 0, INF, dist[now], 1);
	for(int i = first[now]; i; i = next[i])
	if (go[i] != fa && !vis[go[i]]) getdis(go[i], now);
}
inline void getans(const int &now, const int &fa)
{
	if (dis[now] >= K) return;
	if (dis[now] == K - 1 && dist[now] > ans) ans = dist[now], ans1 = 1;
	else if (dis[now] == K - 1 && dist[now] == ans) ans1++;
	if (K - dis[now] - 1 > 0 && a[K - dis[now] - 1] && a[K - dis[now] - 1] + dist[now] > ans)
	{
		ans = a[K - dis[now] - 1] + dist[now];
		tot = 0;
		find(rt[K - dis[now] - 1], 0, INF, ans - dist[now]);
		ans1 = tot;
	}	
	else if (K - dis[now] - 1 > 0 && a[K - dis[now] - 1] && ans - dist[now] > 0)
	{
		tot = 0;
		find(rt[K - dis[now] - 1], 0, INF, ans - dist[now]);
		ans1 += tot;
	}
	for(int i = first[now]; i; i = next[i])
	if (go[i] != fa && !vis[go[i]])
	{
		dis[go[i]] = dis[now] + 1;
		dist[go[i]] = dist[now] + way[i];
		getans(go[i], now);
	}
}
inline void delet(const int &now, const int &fa)
{
	if (dis[now] >= K) return;
	a[dis[now]] = 0;
	insert(rt[dis[now]], 0, INF, dist[now], -1);
	for(int i = first[now]; i; i = next[i])
	if (go[i] != fa && !vis[go[i]]) delet(go[i], now);
}
inline void solve(const int &now)
{
	vis[now] = 1;
	for(int i = first[now]; i; i = next[i])
	if (!vis[go[i]])
	{
		int v = go[i];
		dis[v] = 1;
		dist[v] = way[i];
		getans(v, now);
		getdis(v, now);
	}
	for(int i = first[now]; i; i = next[i])
	if (!vis[go[i]]) delet(go[i], now);
	for(int i = first[now]; i; i = next[i])
	if (!vis[go[i]])
	{
		sroot = MAXX;
		getroot(go[i], now, size[go[i]]);
		solve(root);
	}
}
int main()
{
	freopen("data.in", "r", stdin);
	freopen("data.out", "w", stdout);
	n = get(); m = get(); K = get();
	for(i = 1; i <= m; i ++)
	{
		sta[i].x = get(); sta[i].y = get(); sta[i].z = get();
		sta[i + m].y = sta[i].x; sta[i + m].x = sta[i].y; sta[i + m].z = sta[i].z;
	}
	sort(sta + 1, sta + 1 + m + m, rule);
	for(i = 1; i <= (m << 1); i ++)
		add1(sta[i].x, sta[i].y, sta[i].z);
	t = 0;
	bfs();
	dfs1(1);
	for(i = 1; i <= n; i ++)
		vis[i] = dis[i] = 0;
	sroot = MAXX;
	getroot(1, 0, n);
	solve(root);
	cout << ans << " " << ans1 << endl;
}


發佈了44 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章