• Codeforces835F Roads in the Kingdom


    Description

    link

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

    (nle 2 imes10^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) 距离

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

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

    所以有(maxlimits_ {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 o i-1) 中或者都在 (i+1 o 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) 求直径,然后这里的后面分析的部分其实并不算难

  • 相关阅读:
    context:componentscan 分析
    分布式计算
    分布式数据库
    Windows界面编程第四篇 异形窗体 高富帅版
    图解如何在VC6,VS2008中设置程序条件断点
    makefile的语法以及写法
    KMP字符串模式匹配算法
    VC中操作excel表格
    Windows界面编程第二篇 半透明窗体
    Windows界面编程第一篇 位图背景与位图画刷
  • 原文地址:https://www.cnblogs.com/yspm/p/13461515.html
Copyright © 2020-2023  润新知