zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", ans);
        return 0;
    }
    

      

    禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载 ,用户转载请注明出处:https://www.cnblogs.com/xcysblog/
  • 相关阅读:
    2016-2017(2)数据结构课程小结
    顺序表2种结构体构造方法
    有理数四则运算的实验报告小结
    20165230 结对编程项目-四则运算 阶段总结
    20165230 《Java程序设计》实验二(Java面向对象程序设计)实验报告
    20165230 2017-2018-2 《Java程序设计》第7周学习总结
    20165230 2017-2018-2 《Java程序设计》第6周学习总结
    20165230 《Java程序设计》实验一(Java开发环境的熟悉)实验报告
    20165230 2017-2018-2 《Java程序设计》第5周学习总结
    20165230 2017-2018-2 《Java程序设计》第4周学习总结
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9568426.html
Copyright © 2011-2022 走看看