zoukankan      html  css  js  c++  java
  • bzoj4016 [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) 行,每行三个正整数 (A_i,B_i,C_i(1le A_i,B_ile n,1le C_ile 10000)) ,表示 (A_i)(B_i) 间有一条长度为 (C_i) 的边。数据保证输入的是连通的无向图。

    Output

    输出一行两个整数,以一个空格隔开,第一个整数表示包含 (K) 个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

    Sample

    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
    

    Solution

    首先肯定要把最短路树给建出来。

    然后就是点分傻逼题。 (g[0][i]) 表示包含 (i) 个点的路径的最大长度, (g[1][i]) 表示方案数。大家都会。

    这题出得不太好啊,最短路树的那个完全是恶意增加题目代码复杂度。我还真不信会点分治的人不会构造最短路树……

    不过这几天沉迷字符串,来一道点分写写也是不错的。

    #include<bits/stdc++.h>
    using namespace std;
    
    #define N 30001
    #define ll long long
    
    inline int read() {
    	int x = 0; char ch = getchar(); while (!isdigit(ch))  ch = getchar();
    	while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x;
    }
    
    struct edge { int v, w, next; }e[N << 2], et[N << 2];
    int n, m, K, head[N], tot = 1, headt[N], tott = 1, dis[N], from[N], siz[N], mx[N], sum, root, g[2][N], ans1, ans2, dep[N], fa[N], q[N];
    bool inq[N], vis[N];
    
    inline void add(int u, int v, int w) { e[++tot].v = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot; }
    inline void addt(int u, int v, int w) { et[++tott].v = v, et[tott].w = w, et[tott].next = headt[u], headt[u] = tott; }
    
    void spfa() {
    	deque<int> q; q.push_back(1);
    	for (int i = 2; i <= n; i++) dis[i] = 2000000000;
    	ll sum = 0; int cnt = 1;
    	while (!q.empty()) {
    		int u = q.front(); q.pop_front();
    		if ((ll)dis[u] * cnt > sum) { q.push_back(u); continue; }
    		inq[u] = 0, cnt--, sum -= dis[u];
    		for (int i = headt[u], v; i; i = et[i].next) if (dis[v = et[i].v] > dis[u] + et[i].w) {
    			dis[v] = dis[u] + et[i].w, from[v] = i;
    			if (!inq[v]) {
    				if (q.empty() || dis[v] < dis[q.front()]) q.push_front(v);
    				else q.push_back(v);
    				inq[v] = 1, cnt++, sum += dis[v];
    			}
    		}
    	}
    	for (int i = 1; i <= n; i++) if (from[i])
    		add(et[from[i] ^ 1].v, i, et[from[i]].w), add(i, et[from[i] ^ 1].v, et[from[i]].w);
    }
    
    void getRoot(int u, int fa) {
    	siz[u] = 1, mx[u] = 0;
    	for (int i = head[u], v; i; i = e[i].next) if (!vis[v = e[i].v] && v != fa)
    		getRoot(v, u), siz[u] += siz[v], mx[u] = max(mx[u], siz[v]);
    	if ((mx[u] = max(mx[u], sum - siz[u])) < mx[root]) root = u;
    }
    
    void calc(int u) {
    	for (int i = 1; i <= K; i++) g[0][i] = -2000000000, g[1][i] = 0;
    	for (int i = head[u], v; i; i = e[i].next) if (!vis[v = e[i].v]) {
    		dep[v] = 1, dis[v] = e[i].w, fa[v] = u;
    		int l = 1, r = 1; q[1] = v;
    		while (l <= r) {
    			int x = q[l++];
    			for (int j = head[x], y; j; j = e[j].next) if (!vis[y = e[j].v] && y != fa[x])
    				fa[y] = x, dep[y] = dep[x] + 1, dis[y] = dis[x] + e[j].w, q[++r] = y;
    		}
    		for (int j = 1; j <= r; j++) {
    			int d = dep[q[j]]; fa[q[j]] = 0; if (d >= K) break;
    			if (dis[q[j]] + g[0][K - d - 1] > ans1) ans1 = dis[q[j]] + g[0][K - d - 1], ans2 = g[1][K - d - 1];
    			else if (dis[q[j]] + g[0][K - d - 1] == ans1) ans2 += g[1][K - d - 1];
    		}
    		for (int j = 1; j <= r; j++) {
    			int d = dep[q[j]]; if (d >= K) break;
    			if (d == K - 1) {
    				if (dis[q[j]] > ans1) ans1 = dis[q[j]], ans2 = 1;
    				else if (dis[q[j]] == ans1) ans2++;
    			}
    			else if (dis[q[j]] > g[0][d]) g[0][d] = dis[q[j]], g[1][d] = 1;
    			else if (dis[q[j]] == g[0][d]) g[1][d]++;
    		}
    	}
    }
    
    void solve(int u) {
    	calc(u), vis[u] = 1;
    	for (int i = head[u], v; i; i = e[i].next) if (!vis[v = e[i].v])
    		root = 0, sum = siz[v], getRoot(v, 0), solve(root);
    }
    
    int main() {
    	cin >> n >> m >> K;
    	for (int i = 1, u, v, w; i <= m; i++) u = read(), v = read(), w = read(), addt(u, v, w), addt(v, u, w);
    	spfa(), sum = mx[0] = n, getRoot(1, 0), solve(root);
    	printf("%d %d", ans1, ans2);
    	return 0;
    }
    
  • 相关阅读:
    Springboot+mybatis-plus+mysql+clickhouse集成多数据源
    对集合里每个元素是一个对象,按照对象某一个属性值给这个集合排序
    vue的a-tree-select选择父节点回显的是子节点
    Es简单条件查询
    使用Ant Desigen在vue里面实现分页以及表头的模糊查询
    搭建第一个vue项目
    Address localhost:1099 is already in use
    spring的控制反转DI---基于注解实现
    mybatis下的ResultMap配置一对一以及一对多
    mybatis入门
  • 原文地址:https://www.cnblogs.com/aziint/p/8648902.html
Copyright © 2011-2022 走看看