bzoj4016 最短路路径问题

Time Limit: 5 Sec Memory Limit: 512 MB

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行,每行三个正整数\(A_i,B_i,C_i(1 \le A_i,B_i \le n,1 \le C_i \le 10000)\),表示\(A_i\)和\(B_i\)间有一条长度为\(C_i\)的边。数据保证输入的是连通的无向图。

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 \le 30000,m \le 60000,2 \le K \le n\)。数据保证最短路径树上至少存在一条长度为K的路径。

题解

这题很显然是一道点分治的裸题。

我们可以定义\(g[i]\)表示对于当前\(root\),深度为\(i\)的最大距离以及此距离方案数。

为了求出\(ans\),我们再用\(f[i]\)累积之前的\(g[i]\),这样,我们就可以直接点分治求出答案。

#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int N = 30010;
typedef pair<int, int>pii;
vector<pii> G[N];
int n, m, mv, cnt, root, Siz, K;
int s[N], froot, f[2][N], g[2][N];
bool vis[N];
int to[N << 1], nxt[N << 1], head[N], W[N << 1], totE; #define Adde(a,b,c) (to[totE] = b, nxt[totE] = head[a], W[totE] = c, head[a] = totE++) //dijkstra
priority_queue<pii, vector<pii>, greater<pii> >heap;
int d[N]; void dijkstra() {
memset(d, 127, sizeof d);
heap.push(make_pair(d[1] = 0, 1));
int p, dis, w, v;
while (!heap.empty()) {
dis = heap.top().first, p = heap.top().second;
heap.pop(); if (d[p] ^ dis) continue;
sort(G[p].begin(), G[p].end());
for (int i = 0; i < G[p].size(); ++i)
if (d[v = G[p][i].first] > dis + (w = G[p][i].second)) heap.push(make_pair(d[v] = dis + w, v));
}
} void Build(int u) {
int v; vis[u] = true;
for (int i = 0; i < G[u].size(); ++i)
if (!vis[v = G[u][i].first] && d[v] == d[u] + G[u][i].second) {
Adde(v, u, G[u][i].second); Adde(u, v, G[u][i].second); Build(v);
}
} void getroot(int u, int fa) {
s[u] = 1; int mx = 0, v;
for (int it = head[u]; ~it; it = nxt[it])
if (!vis[v = to[it]] && (v ^ fa)) {
getroot(v, u); s[u] += s[v];
if (s[v] > mx) mx = s[v];
}
if (Siz - mx > mx) mx = Siz - mx;
if (froot > mx) root = u, froot = mx;
} void dfs(int u, int fa, int dep) {
if (dep > K) return; int v;
if (d[u] > g[0][dep]) g[0][dep] = d[u], g[1][dep] = 0;
if (d[u] >= g[0][dep]) ++g[1][dep];
for (int i = head[u]; ~i; i = nxt[i])
if (!vis[v = to[i]] && (v ^ fa)) {
d[v] = d[u] + W[i]; dfs(v, u, dep + 1);
}
} void solve(int u, int S) {
vis[u] = true; f[0][0] = 0; f[1][0] = 1;
int v;
for (int i = head[u]; ~i; i = nxt[i])
if (!vis[v = to[i]]) {
d[v] = W[i]; dfs(v, u, 1);
for (int j = 1; j <= K; ++j) {
if (g[0][j] + f[0][K - j] > mv) mv = g[0][j] + f[0][K - j], cnt = 0;
if (g[0][j] + f[0][K - j] >= mv) cnt += g[1][j] * f[1][K - j];
}
for (int j = 1; j <= K; ++j) {
if (g[0][j] > f[0][j]) f[0][j] = g[0][j], f[1][j] = 0;
if (g[0][j] >= f[0][j]) f[1][j] += g[1][j];
g[0][j] = g[1][j] = 0;
}
}
for (int i = 0; i <= K; ++i) f[0][i] = f[1][i] = 0;
for (int i = head[u]; ~i; i = nxt[i])
if (!vis[v = to[i]]) {
froot = Siz = s[v]; root = 0;
getroot(v, u);
solve(root, s[v]);
}
} int main() {
scanf("%d%d%d", &n, &m, &K);
--K;
int a, b, c;
while (m--) {
scanf("%d%d%d", &a, &b, &c);
G[a].push_back(make_pair(b, c));
G[b].push_back(make_pair(a, c));
}
dijkstra();
memset(head, -1, sizeof head);
Build(1);
memset(vis, 0, sizeof vis);
froot = Siz = n;
getroot(1, root = 0);
solve(root, n);
printf("%d %d\n", mv, cnt);
return 0;
}

bzoj 4016: [FJOI2014]最短路径树问题的更多相关文章

  1. bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)

    4016: [FJOI2014]最短路径树问题 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 426  Solved: 147[Submit][Stat ...

  2. BZOJ 4016: [FJOI2014]最短路径树问题( 最短路 + 点分治 )

    先跑出最短路的图, 然后对于每个点按照序号从小到大访问孩子, 就可以搞出符合题目的树了. 然后就是经典的点分治做法了. 时间复杂度O(M log N + N log N) -------------- ...

  3. BZOJ 4016 [FJOI2014]最短路径树问题 (贪心+点分治)

    题目大意:略 传送门 硬是把两个题拼到了一起= = $dijkstra$搜出单源最短路,然后$dfs$建树,如果$dis_{v}=dis_{u}+e.val$,说明这条边在最短路图内,然后像$NOIP ...

  4. [BZOJ4016][FJOI2014]最短路径树问题(dijkstra+点分治)

    4016: [FJOI2014]最短路径树问题 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 1796  Solved: 625[Submit][Sta ...

  5. 【BZOJ4016】[FJOI2014]最短路径树问题

    [BZOJ4016][FJOI2014]最短路径树问题 题面 bzoj 洛谷 题解 虽然调了蛮久,但是思路还是蛮简单的2333 把最短路径树构出来,然后点分治就好啦 ps:如果树构萎了,这组数据可以卡 ...

  6. 【BZOJ4016】[FJOI2014]最短路径树问题(点分治,最短路)

    [BZOJ4016][FJOI2014]最短路径树问题(点分治,最短路) 题面 BZOJ 洛谷 题解 首先把最短路径树给构建出来,然后直接点分治就行了. 这个东西似乎也可以长链剖分,然而没有必要. # ...

  7. [BZOJ4016][FJOI2014]最短路径树问题

    [BZOJ4016][FJOI2014]最短路径树问题 试题描述 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长 ...

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

    BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治 Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择 ...

  9. [FJOI2014]最短路径树问题 长链剖分

    [FJOI2014]最短路径树问题 LG传送门 B站传送门 长链剖分练手好题. 如果你还不会长链剖分的基本操作,可以看看我的总结. 这题本来出的很没水平,就是dijkstra(反正我是不用SPFA)的 ...

随机推荐

  1. Java基础知识【下】( 转载)

    http://blog.csdn.net/silentbalanceyh/article/details/4608360 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...

  2. Andriod 自定义控件之音频条

    今天我们实现一个直接继承于View的全新控件.大家都知道音乐播放器吧,在点击一首歌进行播放时,通常会有一块区域用于显示音频条,我们今天就来学习下,播放器音频条的实现. 首先我们还是先定义一个类,直接继 ...

  3. C#初学单例模式

    版本1:最简单的单例模式 public class MySingleton { private MySingleton() //构造函数,注意private { } private static My ...

  4. java中使用javamail发送邮件

    1. 电子邮件协议 电子邮件的在网络中传输和网页一样需要遵从特定的协议,常用的电子邮件协议包括 SMTP,POP3,IMAP. 其中邮件的创建和发送只需要用到 SMTP协议,所有本文也只会涉及到SMT ...

  5. 机器学习实战笔记(Python实现)-04-Logistic回归

    --------------------------------------------------------------------------------------- 本系列文章为<机器 ...

  6. python排序之二冒泡排序法

    python排序之二冒泡排序法 如果你理解之前的插入排序法那冒泡排序法就很容易理解,冒泡排序是两个两个以向后位移的方式比较大小在互换的过程好了不多了先上代码吧如下: 首先还是一个无序列表lis,老规矩 ...

  7. Java 加解密技术系列文章

    Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列 ...

  8. What's new in Windows 10 Enterprise with Microsoft Edge.(Windows 10 新功能)

    What's new in Windows 10 Enterprise with Microsoft Edge --带有Edge浏览器的Windows 10 企业版的新功能 本文摘录自公司群发邮件, ...

  9. eclipse安装版本

    http://www.08kan.com/gwk/MzAwOTE3NDY5OA/203521023/1/cdf557d3a1d4535a6c691ce756c3f8b1.html

  10. Linux系统下输出某进程内存占用信息的c程序实现

    在实际工作中有时需要程序打印出某个进程的内存占用情况以作参考, 下面介绍一种通过Linux下的伪文件系统/proc 计算某进程内存占用的程序实现方法. 首先, 为什么会有所谓的 伪文件 呢. Linu ...