Codeforces835F Roads in the Kingdom

Description

link

给定一个基环树,可以在保证树联通的情况之下要求一个最小直径

\(n\le 2\times10^5\)

Solution

这题目就是会说在环上拆掉一条边之后让环上的直径最小

分为几种情况

先说说怎么求基环树的直径吧

也就是\(IOI2008 \ Islands\)

(啊那个题目的主算法是求基环树直径)

首先对于每个点,用树形 \(DP\) 直径和当前树的根节点到子树内的最长链

具体做法如下(自己手动模拟一下其实很可理解)

 inline void prework(int x,int fa,int rt)
    {
        for(int i=1;i<=n;++i) 
        {
            int t=e[i].to; if(t==fa) continue;
            prework(t,x); 
            mx[rt]=max(mx[rt],d[t]+d[x]+e[i].dis);
            d[x]=max(d[x],d[t]+e[i].dis);
        }return ;
    }

然后是基环树找环的问题,直接 \(dfs\)

(引用 \(@jiqimao\) 的话:\(tarjan\) 找环确实大才小用)

然后我们观察如果一条直径经过基环树的话

用 \(d_x\) 表示子树最长链,\(s_x\) 表示环上从环的启示点到 \(x\) 距离

这个起始点不关键,毕竟最后是要前缀和处理掉的

还要加上环型的套路,断开并复制

所以有\(\max\limits_ {y-x< n}\{s_y-s_x+d_x+d_y\}\)

然后枚举每个 \(x\) 的时候需要考虑的只是变量 \(y\)

然后这东西可以看出来是可以单调维护的

然后找出来环,枚举环上的点,计算 \(s_i-d_i\),用单调队列维护

找直径这部分就解决了

\(1.\) 原来的直径和基环树无关

那么直接上原来的直径就完事了

(先上个bzoj island的方式来个求基环树直径)

\(2.\) 原来的直径和基环树是有关的

考虑经过环的情况

如果经过的环的较短的一端,那么显然是不成立的(走长的一端就更大)

那么断掉那一端上的一条边才能保证让直径变小

首先做一下这个树形 \(dp\) 求直径

然后是求所有情况之下 \(\max\{d_{i}+d_{j}+dis_{j,i}\}\) 的 最小值

这里分几种情况讨论一下:(我一开始以为用一个一样的单调队列就能解决……)

假设边 \((i,i-1)\)

\(1.\)首先是两个端点都在 \(1\to i-1\) 中或者都在 \(i+1\to tmp\) 中

那么可以直接把式子表示出来然后推一推,发现对于枚举的量,都有一个固定的另一边的量,那么记录一下

\(2.\) 直径分别在两边

接着表示一下式子,就是\(s_{tmp}-s_r+s_l+d_l+d_r\)

还是可以分组用变量记录最大值的

\(ans\) 对于所有情况的取 \(max\) 就好了

所以 \(code\) 就慢慢来吧

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define For(i,a,b) for(int i=a;i<=b;++i) 
namespace yspm{
    inline int read()
    {
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int N=2e5+10;
    struct node{
        int to,dis,nxt;
    }e[N<<1];
    int head[N],cnt=1,ans;
    inline void add(int u,int v,int w)
    {
        e[++cnt].dis=w; e[cnt].to=v; e[cnt].nxt=head[u];
        return head[u]=cnt,void();
    }
    deque<int>q ;
    int vis[N],n,mx[N],d[N],s[N],g[N],tmp,l0[N],l1[N],r1[N],r0[N];
    pair<int,int> h[N<<1];
    inline int dfs(int x,int pre)
    {
        if(vis[x]==1) return x;
        vis[x]=1;
        for(int i=head[x];i;i=e[i].nxt)
        {
            if(i==(pre^1)) continue;
            int t=e[i].to,ret=dfs(t,i); 
            if(ret)
            {
                h[++tmp]=make_pair(x,e[i].dis);
                return ret==x?0:ret;
            }
        }
        return 0;
    }
    inline void get(int x,int fa,int rt)
    {
        for(int i=head[x];i;i=e[i].nxt)
        {
            int t=e[i].to; if(t==fa||vis[t]==2) continue;
            get(t,x,rt);
            mx[rt]=max(mx[rt],d[x]+d[t]+e[i].dis);
            ans=max(ans,mx[rt]);
            d[x]=max(d[x],d[t]+e[i].dis);
        }return ;
    }
    signed main()
    {
        n=read();
        for(int i=1,u,v,w;i<=n;++i) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
        dfs(1,-1);  //cout<<tmp<<endl;
        for(int i=1;i<=tmp;++i) vis[h[i].first]=2;
        for(int i=1;i<=tmp;++i) get(h[i].first,0,h[i].first),ans=max(ans,mx[i]);
        For(i,1,tmp) s[i]=s[i-1]+h[i].second; 
        int ans2=1e18+10,tm=-1e18-10;
        l0[0]=l1[0]=r1[tmp+1]=r0[tmp+1]=-1e18-10;
        For(i,1,tmp)//维护这里的f[i]+v[i]
        {
            l0[i]=max(tm+d[h[i].first]+s[i],l0[i-1]);
            l1[i]=max(l1[i-1],s[i]+d[h[i].first]);
            tm=max(tm,d[h[i].first]-s[i]);
        }tm=-1e18-10;
        for(int i=tmp;i>=1;--i) 
        {
            r0[i]=max(r0[i+1],tm+d[h[i].first]-s[i]);
            r1[i]=max(r1[i+1],s[tmp]-s[i]+d[h[i].first]);
            tm=max(tm,d[h[i].first]+s[i]);
        }
        //For(i,1,tmp) cout<<d[h[i].first]<<" "; cout<<endl;
        //For(i,1,tmp) cout<<h[i].first<<" "; cout<<endl;
        //For(i,1,tmp) cout<<h[i].first<<" "; cout<<endl;
        //For(i,1,tmp) cout<<l0[i-1]<<" "; cout<<endl;
        //For(i,1,tmp) cout<<l0[i]<<" "; cout<<endl;
        //For(i,1,tmp) cout<<l1[i-1]+r1[i]<<" "; cout<<endl;
        For(i,1,tmp) ans2=min(ans2,max(l0[i-1],max(r0[i],l1[i-1]+r1[i])));
        
        cout<<max(ans,ans2)<<endl;
        return 0;
    }
}
signed main(){return yspm::main();}

总结

首先是树形 \(DP\) 求直径,然后这里的后面分析的部分其实并不算难

上一篇:终于安装上TensorFlow了,孩子泪目


下一篇:pycharm问题解析(connecting to console)