小学生的愤怒(1)

六月ゝ 毕业季﹏ 提交于 2020-01-23 21:42:06

最近,@十一维的智子发送了一篇关于NOIP(没错,这货起死回生了)的文章。
相信小学生党们已经怒了…
没关系!今天我们就用一道CTSC的原题反击这个博主。
开始战斗!
[CTSC2017]网络
题目描述
一个一般的网络系统可以被描述成一张无向连通图。图上的每个节点为一个服务器,连接服务器与服务器的数据线则看作图上的一条边,边权为该数据线的长度。两个服务器之间的通讯距离定义为其对应节点之间最短路的长度。

现在,考虑一个当前图结构为树的网络系统。你作为该网络系统的管理员,被要求在这个系统中新加入一条给定长度的数据线。数据线可以连在任意两个服务器上。你的任务是,求出在所有合法的方案中,通讯距离最远的两个服务器之间的最小距离。

输入格式
输入包含多组数据。对于每组数据,输入的第一行包含二个正整数 N, L, 其中 N 表示服务器个数,L 为新加入数据线的长度。

接下来 n − 1 行,第 i 行有三个正整数 ai, bi, li,表示有一条长度为 li 的数据线连接服务器 ai, bi。服务器的编号为 1 ∼ N。

输入的末尾以两个 0 作为结束。

输出格式
对于每组数据,输出一行一个整数,描述在所有合法的方案中,通讯距离最远的两个服务器之间的最小距离。

输入输出样例
输入 #1复制

7 1
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
0 0

输出 #1

3

输入 #2

6 26
1 2 66
2 3 11
3 4 73
2 5 77
3 6 33
10 47
1 2 86
2 3 69
3 4 41
4 5 26
5 6 41
2 7 73
3 8 77
4 9 2
5 10 65
0 0

输出 #2

143
232

说明/提示
一共有 20 个测试点。编号为 1 ∼ 20。每个测试点为 5 分。
保证在任一测试点中,数据的组数不会超过 15,且所有数据的 N 之和不超过数据范围中标明的 N 的最大值的 5 倍。
保证所有的输入数据均为不超过 2^31 − 1 的非负整数,保证 N ≥ 1。
保证数据合法。

给定一棵带权树,你可以在某对点之间连一条长度为len的边,最小化任意两点间最短路的最大值。

肯定要二分。

有一个重要的结论:我们连边的端点一定在直径上。证明:

首先有一个很容易自己证明的性质:修改后最长的最短路一定和直径有交集。我们以一段不在直径上为例:

无标题.png

红色是直径,黑色是直径上挂着的子树,蓝色是最优解的边。那么我们将蓝色边的右端点上移到直径上的对应点(设为A),答案不会变差。因为所有从A子树里延伸过来的最长链一定不长于A点右侧的直径,所以虽然黑色子树里的点多走了一点,但不会改变答案。

然后我们继续思考:找出任何一条直径,然后我们限制加入新边后的距离\le mid≤mid,我们可以这样表示直径上两个点的距离(从左到右重新标号了),设sum为直径边权的前缀和,dis为子树里的最长链,设答案两点为a,b(a<b)a,b(a<b):min(dis[i]-sum[i]+dis[j]+sum[j],dis[i]+|dis[i]-dis[a]|+dis[j]+|dis[j]-dis[b]|)
min(dis[i]−sum[i]+dis[j]+sum[j],dis[i]+∣dis[i]−dis[a]∣+dis[j]+∣dis[j]−dis[b]∣)
使得上式\le mid≤mid,注意前一半和选择没有关系,我们可以用双指针,找出前一半不满足要求的pair,它们必须满足后一个要求。

观察到绝对值,我们有一个套路:枚举绝对值内部的符号,当我们枚举的情况和实际不符时,并不会改变答案,因为它的限制变得更弱了。我们把4种情况的式子写出来,发现是形如sum[a]\pm sum[b] \ge \tt{or}\le …sum[a]±sum[b]≥or≤…
我们求出\ge v≥v限制的v最大值,\le v≤v限制的v的最小值(可能需要使用树状数组,因为别忘了还有i<ji<j这个条件呢)

于是就变成求是否存在(a,b)(a,b),满足上述四个条件了,维护四个指针,判一下区间的交集即可。

#define N 100005
const LL inf=1e16;
// WARN long long !
int len;
vector< pii > E[N];
LL mxd[N]; int mxdk[N];
void dfs(int x,int _fa)
{
    mxd[x]=mxdk[x]=0;
    for(solid it:E[x])
    {
        int v=it.fi,w=it.se;
        if(v==_fa) continue;
        dfs(v,x);
        if(ckmax(mxd[x],mxd[v]+w)) mxdk[x]=v;
    }
}
template<LL f[]>il bool cmp(const int a,const int b) {return f[a]<f[b];}
LL sum[N],f[N],g[N]; int cnt;
int idf[N],idg[N];
void prework()
{
    dfs(1,0);
    int x=1; while(mxdk[x]) x=mxdk[x];
    dfs(x,0);
    while(x)
    {
        int nx=0; LL dis=0;
        ++cnt;
        for(solid it:E[x])
        {
            int v=it.fi,w=it.se;
            if(mxdk[v]==x) continue;
            if(mxdk[x]==v) nx=v,sum[cnt+1]=sum[cnt]+w;
            else ckmax(dis,mxd[v]+w);
        }
        f[cnt]=dis+sum[cnt],g[cnt]=dis-sum[cnt];
        x=nx;
    }
    for(ri i=1; i<=cnt; ++i) idf[i]=idg[i]=i;
    sort(idf+1,idf+1+cnt,cmp<f>),sort(idg+1,idg+1+cnt,cmp<g>);
}
struct BIT
{
    LL tre[N];
    il void upd(int x,const LL &v)
    {
        for(; x<=cnt; x+=x&-x) ckmax(tre[x],v);
    }
    il LL ask(int x)
    {
        LL res=-inf;
        for(; x; x-=x&-x) ckmax(res,tre[x]);
        return res;
    }
} bf,bg;
il void clear2() {mem(bf.tre,0xcf),mem(bg.tre,0xcf);}
bool check(const LL &mid)
{
    clear2();
    int p=cnt+1;
    LL l00=-inf,l01=-inf,l10=inf,l11=inf;
    for(ri i=1; i<=cnt; ++i)
    {
        while(p>1&&g[idg[p-1]]+f[idf[i]]>mid)
        {
            --p;
            bf.upd(idg[p],f[idg[p]]);
            bg.upd(idg[p],g[idg[p]]);
        }
        LL t=bf.ask(idf[i]-1);
        ckmax(l00,t+f[idf[i]]);
        ckmax(l01,t+g[idf[i]]);
        t=bg.ask(idf[i]-1);
        ckmin(l10,-t-f[idf[i]]);
        ckmin(l11,-t-g[idf[i]]);
    }
    l00+=len-mid,l01+=len-mid;
    l10+=mid-len,l11+=mid-len;
    if(l00>l11||l01>l10) return 0;
    p=cnt+1; int q=cnt,r=0,s=1; // [p,q], [s,r]
    for(ri i=1; i<=cnt; ++i)
    {
        while(p>1&&sum[i]+sum[p-1]>=l00) --p;
        while(q>=1&&sum[i]+sum[q]>l11) --q;
        while(r<cnt&&sum[i]-sum[r+1]>=l01) ++r;
        while(s<=cnt&&sum[i]-sum[s]>l10) ++s;
        if(p<=q&&s<=r&&!(q<s||r<p)) return 1;
    }
    return 0;
}
void solve()
{
    LL l=1,r=inf,ans=-1;
    while(l<=r)
    {
        LL mid=(l+r)>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    out(ans);
}
int n;
void clear()
{
    cnt=0;
    for(ri i=1; i<=n; ++i) E[i].clear();
}
signed main()
{
#ifdef M207
    freopen("in.in","r",stdin);
    // freopen("ot.out","w",stdout);
#endif
    while(in(n,len),n)
    {
        clear();
        for(ri i=1,a,b,c; i<n; ++i)
        {
            in(a,b,c);
            E[a].pb(mp(b,c)),E[b].pb(mp(a,c));
        }
        prework();
        solve();
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!