BZOJ1999: [Noip2007]Core树网的核 & BZOJ2282: [Sdoi2011]消防 & 弱化版 on Luogu1099: 树网的核

答案必然是在直径上选取一段路径

如果不懂请自行考虑证明,需要注意的是证明过程中主要用到树的直径是最长链的性质

先考虑两个点都不在直径上,那距离最大值一定是直径的某一段
如果选择一个点在直径上,会使答案更优
两个点都在直径上的话,考虑直径是最长链的性质

最直接的做法就是 O(n^3) ,绝对的模拟一遍操作

略微有点思考后的做法就是 O(n^2) 贪心,为了使到其他点距离尽可能的小,选择恰好距离小于等于 s 的点对

这都是可以水过弱化版的

就下来考虑更优的做法

不难发现答案具有单调性,可以二分答案, O(n*log(Σedge_i.val))

做法是从直径两端 l 和 r 分别向直径中间跳,跳至距直径距离小于等于 mid 的最远点 x 和 y,这对点作为此次二分的路径

因为直径是最长链,所以 l 到 x 之间的子树到 x 的距离不会大于 dist(l, x) ,对于 r 和 y 同理

只需要再保证 dist(x, y) <= s 即可

二分的范围是 [直径上任意一点到其他点的最短距离,直径长度]

可能不太准但复杂度是可过的

继续深究性质,时间复杂度可优化到 O(n) (反正我是不会

设直径由点 u_1, u_2, u_3, ..., u_t 组成

f[u_i] 表示从点 u_i 出发,不经过直径上的其他节点, 所到的最远点的距离

那么以 u_i, u_j 为端点的路径的答案就是 max(max{f[u_k]}, dist(u_1, u_i), dist(u_j, u_t)) (i <= k <= j)

这样就可以单调队列来求了

还可以注意到的是,利用上边二分答案做法中证明的性质,刚才的式子就可以替换为 max(max{f[u_k]}, dist(u_1, u_i), dist(u_j, u_t)) (1 <= k <= t)

//因为多的那些是不比后边的两项大的

那么就有一项是固定的了

那么我们就可以枚举直径上的每个点,贪心的选到与它距离不超过 s 的最远的点,之后按上述式子判断即可

成功 O(n) 解决

upd:有一些小细节,在直径上取一段的时候,这并不是很好搞,需要把更新当前点最远距离的 from 记录下来,然后就可以愉快的从直径中选出想要的一段了

我什么时候能想到这么神仙的做法就好了啊


代码(二分版):

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<queue>
using namespace std;

typedef long long ll;
const int MAXN = 300005;

struct EDGE{
	int nxt, to, val;
	EDGE(int NXT = 0, int TO = 0, int VAL = 0) {nxt = NXT; to = TO; val = VAL;}
}edge[MAXN << 1];
int n, s, p, q, totedge, maxpt, top ,maxdis;
int frm[MAXN], head[MAXN], stk[MAXN], dst[MAXN];
bool vis[MAXN], ind[MAXN];

inline int rd() {
	register int x = 0;
	register char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) {
		x = x * 10 + (c ^ 48);
		c = getchar();
	}
	return x;
}
inline void add(int x, int y, int v) {
	edge[++totedge] = EDGE(head[x], y, v);
	head[x] = totedge;
	return;
}
inline void bfs(int bgn) {
	queue<int> q;
	dst[bgn] = 0;
	q.push(bgn);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		if(dst[x] > dst[maxpt]) maxpt = x;
		for(int i = head[x]; i; i = edge[i].nxt) if(dst[edge[i].to] == 0x3f3f3f3f) {
			int y = edge[i].to;
			frm[y] = x;
			if(ind[y]) dst[y] = dst[x];
			else dst[y] = dst[x] + edge[i].val;
			q.push(y);
		}
	}
	return;
}
inline bool chk(int mid) {
	int l = 1, r = top;
	while(stk[1] - stk[l + 1] <= mid && l + 1 <= top) ++l;
	while(stk[r - 1] <= mid && r - 1 >= 1 && r - 1 >= l) --r;
	return (stk[l] - stk[r] <= s);
}
inline void hfs(int l, int r) {
	int ans = 0, mid;
	while(l <= r) {
		mid = ((l + r) >> 1);
		if(chk(mid)) r = mid - 1;
		else l = mid + 1;
	}
	printf("%d\n", l);
	return;
}

int main() {
	n = rd(); s = rd();
	for(int i = 1; i <= n; ++i) dst[i] = 0x3f3f3f3f;
	register int xx, yy, vv;
	for(int i = 1; i < n; ++i) {
		xx = rd(); yy = rd(); vv = rd();
		add(xx, yy, vv); add(yy, xx, vv);
	}
	bfs(1); p = maxpt; maxpt = 0;
	for(int i = 1; i <= n; ++i) dst[i] = 0x3f3f3f3f;
	bfs(p); q = maxpt;
	maxdis = stk[++top] = dst[q];
	int tmp = q; ind[tmp] = true;
	while(tmp != p) {
		stk[++top] = dst[frm[tmp]];
		tmp = frm[tmp]; ind[tmp] = true;
	}
	maxpt = 0;
	for(int i = 1; i <= n; ++i) dst[i] = 0x3f3f3f3f;
	bfs(tmp);
	hfs(dst[maxpt], maxdis);
	return 0;
}

代码(O(n)版):

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<queue>
using namespace std;
 
const int MAXN = 500005;
 
struct EDGE{
    int nxt, to, val;
    EDGE(int NXT = 0, int TO = 0, int VAL = 0) {nxt = NXT; to = TO; val = VAL;}
}edge[MAXN << 1];
int n, s, totedge, mxpt, p, q, top, lmt, ans = 0x7fffffff;
int head[MAXN], dst[MAXN], frm[MAXN];
pair<int,int> stk[MAXN];
bool ind[MAXN];
queue<int> que;
 
inline int rd() {
    register int x = 0;
    register char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) {
        x = x * 10 + (c ^ 48);
        c = getchar();
    }
    return x;
}
inline void add(int x, int y, int v) {
    edge[++totedge] = EDGE(head[x], y, v);
    head[x] = totedge;
    return;
}
inline void bfs(int bgn) {
    for(int i = 1; i <= n; ++i) dst[i] = 0x7fffffff;
    dst[bgn] = 0;
    que.push(bgn);
    while(!que.empty()) {
        int x = que.front(); que.pop(); if(dst[x] > dst[mxpt]) mxpt = x;
        for(int i = head[x]; i; i = edge[i].nxt) if(dst[edge[i].to] == 0x7fffffff) {
            int y = edge[i].to;
            frm[y] = x;
            if(ind[y]) dst[y] = dst[x];
            else dst[y] = dst[x] + edge[i].val;
            que.push(y);
        }
    }
    return;
}
void dfs(int x, int fa) {
    dst[x] = 0;
    for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
        int y = edge[i].to;
        dfs(y, x);
        if(ind[y]) dst[x] = max(dst[x], dst[y]);
        else dst[x] = max(dst[x], dst[y] + edge[i].val);
    }
    return;
}
 
int main() {
    n = rd(); s = rd();
    register int xx, yy, vv;
    for(int i = 1; i < n; ++i) {
        xx = rd(); yy = rd(); vv = rd();
        add(xx, yy, vv); add(yy, xx, vv);
    }
    bfs(1); p = mxpt; mxpt = 0;
    bfs(p); q = mxpt;
    stk[++top] = make_pair(mxpt, dst[mxpt]); ind[mxpt] = true;
    while(mxpt != p) {
        mxpt = frm[mxpt];
        stk[++top] = make_pair(mxpt, dst[mxpt]);
        ind[mxpt] = true;
    }
    dfs(p, 0);
    for(int i = 1; i <= top; ++i) lmt = max(lmt, dst[stk[i].first]);
    int lptr, rptr = 0, cur;
    for(lptr = 1; lptr <= top; ++lptr) {
        cur = lmt;
        while(stk[lptr].second - stk[rptr + 1].second <= s && rptr + 1 <= top) ++rptr;
        cur = max(cur, max(stk[1].second - stk[lptr].second, stk[rptr].second));
        ans = min(ans, cur);
    }
    printf("%d\n", ans);
    return 0;
}

  

 

posted @ 2018-08-31 22:04  EvalonXing  阅读(174)  评论(0编辑  收藏  举报