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