HDU 4871 Shortest-path tree (树分治+SPFA)



题意:给出一个连通的无向图, 然后找一颗生成树, 生成树满足点1到每个点的书上距离都是原图中的最短路, 然后在满足最短路的条件下, 点1到每个点的树上路径的序列字典序最小

找出这个树之后, 给出K询问树上有K个结点的路径的最大边权值, 以及这样的最大边权值的路径的方案数。

思路:spfa的过程中建树,建好树后点分治。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 31000;
const int MAXM = 61000;
const int INF = 0x3f3f3f3f;
int n, m, k, tot, tot2;
int head[MAXN], head2[MAXN];
struct Edge {
	int to, next, w;
} edge[MAXM*2], edge2[MAXN*2];
void addEdge(int u, int v, int w) {
	edge[tot].to = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot++;
}
void addEdge2(int u, int v, int w) {
	edge2[tot2].to = v;
	edge2[tot2].w = w;
	edge2[tot2].next = head2[u];
	head2[u] = tot2++;
}
int pa[MAXN], lex[MAXN], ed[MAXN];
LL d[MAXN];
bool spfa(int start, int n) {
	bool vis3[MAXN];
	memset(vis3, 0, sizeof(vis3));
	for(int i = 1; i <= n; i++) d[i] = INF;
	vis3[start] = 1;
	d[start] = 0;
	queue<int> q;
	while(!q.empty()) q.pop();
	q.push(start);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis3[u] = false;
		for(int i = head[u]; i != -1; i = edge[i].next) {
			Edge e = edge[i];
			int v = e.to;
			if(d[v]>d[u]+e.w || d[v]==d[u]+e.w&&lex[u]<lex[v]) {
				d[v] = d[u] + e.w;
				pa[v] = u;
				if(u == 1) lex[v] = v;
				else lex[v] = lex[u];
				ed[v] = e.w;
				if(!vis3[v]) {
					vis3[v] = true;
					q.push(v);
				}
			}
		}
	}
	return true;
} 
void initTree() {
	spfa(1, n);
	for(int i = 2; i <= n; i++) {
		addEdge2(i, pa[i], ed[i]);
		addEdge2(pa[i], i, ed[i]);
	}
}
//以上建树部分,以下树分治
int ans, num, root, des[MAXN], bal[MAXN], cnt[MAXN], tim[MAXN], clk;
LL dist[MAXN];
bool vis[MAXN];
int get_size(int cur, int fa) {
	int ans = 1;
	for(int i = head2[cur]; i != -1 ; i = edge2[i].next) {
		int u = edge2[i].to;
		if(u==fa || vis[u]) continue;
		ans += get_size(u, cur);
	}
	return ans;
}
void get_root(int cur, int fa, int tot) {
	des[cur] = 1;
	bal[cur] = 0;
	for(int i = head2[cur]; i != -1 ; i = edge2[i].next) {
		int u = edge2[i].to;
		if(vis[u] || u==fa) continue;
		get_root(u, cur, tot);
		des[cur] += des[u];
		bal[cur] = max(bal[cur], des[u]);
	}
	bal[cur] = max(bal[cur], tot-des[cur]);
	if(bal[cur] < bal[root]) root = cur;
} 
void dfs(int cur, int fa, int dep, int w, int op) {
	if(!op) {
		if(k>=dep && tim[k-dep+1]==clk) {
			if(dist[k-dep+1]+w == ans) num += cnt[k-dep+1];
			else if(dist[k-dep+1]+w > ans){
				ans = dist[k-dep+1]+w;
				num = cnt[k-dep+1];
			}
		}
	}
	else {
		if(tim[dep]!=clk) dist[dep] = w, cnt[dep] = 1, tim[dep]=clk;
		else if(dist[dep] < w) dist[dep] = w, cnt[dep] = 1;
		else if(dist[dep] == w) cnt[dep]++;
	}
	if(k<=dep) return;
	for(int i = head2[cur]; i != -1 ; i = edge2[i].next) {
		int u = edge2[i].to;
		if(u==fa || vis[u]) continue;
		dfs(u, cur, dep+1, w+edge2[i].w, op);
	}
}
void solve(int cur) {
	dist[1] = 0;
	++clk;
	cnt[1] = 1;
	tim[1] = clk;
	vis[cur] = 1;
	for(int i = head2[cur]; i != -1 ; i = edge2[i].next) {
		int u = edge2[i].to;
		if(vis[u]) continue;
		dfs(u, cur, 2, edge2[i].w, 0);
		dfs(u, cur, 2, edge2[i].w, 1);
	}
	for(int i = head2[cur]; i != -1 ; i = edge2[i].next) {
		int u = edge2[i].to;
		if(vis[u]) continue;
		int sz = get_size(u, cur);
		if(sz < k) continue;
		root = 0; get_root(u, cur, sz);
		solve(root);
	}
} 
void init() {
	memset(head, -1, sizeof(head));
	memset(head2, -1, sizeof(head2));
	memset(vis, 0, sizeof(vis));
	memset(tim, 0, sizeof(tim));
	clk = ans = num = tot = tot2 = 0;
	bal[0] = INF;  
}
template<class T>  
inline bool read(T &n){  
    T x = 0, tmp = 1; char c = getchar();  
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();  
    if(c == EOF) return false;  
    if(c == '-') c = getchar(), tmp = -1;  
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();  
    n = x*tmp;  
    return true;  
}  

int main() {
    //freopen("input.txt", "r", stdin);
    //cout << INF << endl;
	int T; cin >> T;
	while(T--) {
		scanf("%d%d%d", &n, &m, &k);
		init();
		for(int i = 1, u, v, d; i <= m; i++) {
			read(u); read(v); read(d);
			addEdge(u, v, d);
			addEdge(v, u, d);
		}
		initTree();
		root = 0; get_root(1, 0, n);
		solve(root);
		printf("%d %d\n", ans, num);
	}
    return 0;
}


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值