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;
}