【b704 && BZOJ 1999】树网的核

来源:互联网 发布:nginx 缓存设置 编辑:程序博客网 时间:2024/05/18 20:47

【题目链接】: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\n",ans);    return 0;}
0 0