• 【b704 && BZOJ 1999】树网的核


    【题目链接】:http://noi.qz5z.com/viewtask.asp?id=b704 &&http://www.lydsy.com/JudgeOnline/problem.php?id=1999

    【题意】

    给你一棵树;
    让你找出所有的直径;
    并在这些直径上面选取连续的一段;
    使得它的偏心距最小;

    【题解】

    这题有个思维量比较大的点就是;
    多条直径,只要选取任意一条就好;
    网上找到了很多分析;
    感觉这个说得比较清楚吧;

    /*
    证明:首先,如图,如果ABCD和FBCE都是直径的话,则AB=FB,CD=CE(如果不然,可设AB>FB,则FBCE<ABCE,矛盾!)。这样,ABCE,FBCD也都是直径。我们给BC起个名字叫“公共段”。由连通性和路径的不唯一性,公共段必然存在。
    考虑ecc的定义,路径的ecc是指所有的点到路径的距离的最大值。
    核指的是直径上长度满足约束的ECC最小的子路经。假如根据直径ABCE算得的core是GHBI,路径GHBI的ecc就是max{BF,AG,DI, EI},这个最大值取到了最小。由于DC=EC,也就是说,如果路径和公共段有交集,公共段的一端上,不包含CORE的直径是可以任选的。换言之,如果max{BF,AG,DI, EI}取到了最小值,必有 max{BF,AG,ID}=max{BF,AG,DI, EI},此时用AB替换BF,则BF=AB>AG,ecc=max{BF,ID}。也就是说,如果路径和公共段有交集,实际计算max时,只需要计算路径在公共段上的部分的ecc,然后和公共段两端的路径长取一遍MAX就行了。
    下面证明,使得ecc取到最小的core必然和公共段有交集。设没有交集,则必然有一条直径和这个core没有交集,此core的ecc就至少严格大于公共段长度+除去公共段的半条路径长度,然而,公共段上的点到其他点的最长距离,最大不会大于这个长度,这与ecc最小矛盾!
    通过上面的论述,得出core只与公共段有关,也就是说引理成立。所以在计算时任选一条直径即可,}
    */


    知道上面这个结论之后,瞬间压力就小了很多了;
    再贪心一下;
    可以想见,肯定是这段路径的长度越长越好;
    (如果不是最长的,那么就会有一段多出来,所以感觉上是尽可能地长)
    所以每次枚举这段路径的起点,终点的话可以根据s来确定,越长越好;
    当然在枚举之前,先找出任意一条直径;然后把直径上的点标记一下;
    然后从直径上的点开始进行dfs;在不经过直径的情况下,看看这个点最远能走多远;则这个长度就是这个点的偏心距了;
    (整条直径的偏心距就是这个直径上的所有的点的偏心距的最大值->直径的偏心距的等价含义);
    这个可以预处理出来;->设为mmax[n]
    然后回到枚举那段
    枚举了起点s,和终点t;
    然后直径的左端点为l,右端点为r;
    则这段路径s..t的偏心距为max(dis[l]-dis[s],dis[r]-dis[t],mmax[s..t]中的最大值);
    (dis[x]是这段路径上的点x到直径的左端点l的距离);
    因为在求mmax的时候没有考虑直径上的点,所以会漏掉这种情况.就是直径的左端点和右端点离这个核最远的情况.
    这里s..t可以像窗口一样往右移动;
    (保持前一次的右端点t不动,左端点右移,然后根据新的左端点调整右端点);
    可以想到用单调队列来优化;
    这里dis[l]-dis[s],dis[r]-dis[t]都是定值了,所以不用管;
    直接维护mmax单调递减就好;
    (求直径的话,随便从一个点开始dfs,找离他最远的点u1,然后从u1再重复上述过程,找到u2,则u1-u2就是一条直径);

    【完整代码】

    #include <bits/stdc++.h>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define LL long long
    #define rep1(i,a,b) for (int i = a;i <= b;i++)
    #define rep2(i,a,b) for (int i = a;i >= b;i--)
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define se second
    #define rei(x) scanf("%d",&x)
    #define rel(x) scanf("%I64d",&x)
    
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pll;
    
    const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
    const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
    const double pi = acos(-1.0);
    const int MAXN = 5e5+100;
    
    struct abc
    {
        int nex,en,w;
    };
    
    int dis[MAXN],n,s,tot,fir[MAXN],path[MAXN],len,lmax[MAXN],rmax[MAXN],mmax[MAXN];
    int dl[MAXN],l,r;
    bool bo[MAXN];
    abc bian[MAXN*2];
    
    void add(int x,int y,int z)
    {
        bian[++tot].nex = fir[x];
        fir[x] = tot;
        bian[tot].en = y,bian[tot].w = z;
    }
    
    void dfs(int x,int fa,int arr[])
    {
        for (int i = fir[x];i;i=bian[i].nex)
        {
            int y = bian[i].en;
    
            if (y==fa) continue;
    
            arr[y] = arr[x] + bian[i].w;
            dfs(y,x,arr);
        }
    }
    
    bool get_path(int x,int aim,int fa)
    {
        if (x==aim)
        {
            path[++len] = x;
            return true;
        }
        for (int i = fir[x];i;i=bian[i].nex)
        {
            int y = bian[i].en;
            if (y==fa) continue;
    
            if (get_path(y,aim,x))
            {
                path[++len] = x;
                return true;
            }
        }
        return false;
    }
    
    int gainecc(int x,int fa)
    {
        int ret = 0;
        for (int i = fir[x];i;i = bian[i].nex)
        {
            int y = bian[i].en;
    
            if (bo[y] || y==fa) continue;
    
            ret = max(gainecc(y,x)+bian[i].w,ret);
        }
        return ret;
    }
    
    int main()
    {
        //freopen("F:\rush.txt","r",stdin);
        rei(n);rei(s);
        rep1(i,1,n-1)
        {
            int x,y,z;
            rei(x);rei(y);rei(z);
    
            add(x,y,z),add(y,x,z);
        }
    
        int q,w;
    
        dis[1] = 0;
        dfs(1,0,dis);
    
        q = 1;
        rep1(i,2,n)
            if (dis[i]>dis[q])
                q = i;
    
        dis[q] = 0;
        dfs(q,0,dis);
    
        w = 1;
        rep1(i,2,n)
            if (dis[i]>dis[w])
                w = i;
    
        get_path(w,q,0);
    
        rep1(i,1,len)
        {
            lmax[i] = dis[path[i]]-dis[q];
            rmax[i] = dis[w]-dis[path[i]];
        }
    
        rep1(i,1,len)
            bo[path[i]] = true;
        rep1(i,1,len)
            mmax[path[i]] = gainecc(path[i],0);
    
        l = 1,r = 0;
        int j = 0,ans = -1;
        rep1(i,1,len)
        {
            while (j+1<=len && dis[path[j+1]]-dis[path[i]]<=s)
            {
                j++;
                while (r>=l && mmax[path[j]]>=mmax[path[dl[r]]]) r--;
                dl[++r] = j;
            }
    
            while (dl[l] < i) l++;
    
            int cal = max(mmax[path[dl[l]]],max(lmax[i],rmax[j]));
    
            if (ans == -1 || cal < ans)
                ans = cal;
        }
    
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    [AH2017/HNOI2017]礼物
    [八省联考2018]林克卡特树lct
    [洛谷P4847]银河英雄传说V2
    [洛谷P4999]烦人的数学作业
    [洛谷P4171][JSOI2010]满汉全席
    [CF785E]Anton and Permutation
    [洛谷P2511][HAOI2008]木棍分割
    [洛谷P4430]小猴打架
    [UVA307]小木棍 Sticks
    [LOJ #6433]「PKUSC2018」最大前缀和
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7626630.html
Copyright © 2020-2023  润新知