Live2d Test Env

BZOJ - 3242 :快餐店 (基环树DP) 最小化半径

题意:给定N点N边的无向连通图,现在让你在图中找一点作为餐厅,使得最远点距离这点最近。

思路:为了保留整数,我们求最小直径,最后去除2。  直径来源于两部分:

        1,在外向树中; 那么就是树的直接,一棵树求直径直接跑一边DP就可以了。(或者两遍BFS吧,麻烦一点而已),假设最大值为ans1。

        2,可能通过了环。 一定有环上的边不在直接上,我们枚举这个断边。 那么现在就相当于一个X元环,环上的边有权值,X个点也各有权值,现在枚举每个断边,变成一条带枝桠的链,让你求ans2=min(最长链)。

答案就是二者取max(ans1,ans2)。

对于第二份部分,我们可以通过记录一个前缀和,一个后缀和,一个前缀答案,后缀答案,取得。

 

而bzoj1791让你求基环树的直径,那就是所有位置取max,通过环的部分可以加倍成链用单调队列搞定。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=200010;
int Laxt[maxn],Next[maxn],To[maxn],Len[maxn],cnt=1;
int r[maxn],vis[maxn],tot;
ll L[maxn],sum[maxn],ans,dp[maxn],p[maxn];
void add(int u,int v,int w)
{
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; Len[cnt]=w;
}
bool dfs(int u,int f)
{
    if(vis[u]==1)
      { vis[u]=2; r[++tot]=u;L[tot]=Len[f]; return true;} //开始出现环
    vis[u]=1;
    for(int i=Laxt[u];i;i=Next[i]){
        if(i==(f^1)) continue;
        if(dfs(To[i],i)) {
            if(vis[u]==2) return false; //环遍历完了
            else {vis[u]=2; r[++tot]=u; L[tot]=Len[f]; return true;}
        }
    }
    return false;
}
void treedp(int u,int f) //得到外向树的直径
{
    for(int i=Laxt[u];i;i=Next[i]){
        if(i==(f^1)||vis[To[i]]==2) continue;
        treedp(To[i],i);
        ans=max(ans,dp[u]+Len[i]+dp[To[i]]);
        dp[u]=max(dp[u],dp[To[i]]+Len[i]);
    }
}
ll A[maxn],B[maxn],C[maxn],D[maxn];
void solve()
{
    ll P=0,Mx=0;
    rep(i,1,tot) p[i]=dp[r[i]];
    rep(i,1,tot){
         P+=L[i-1];
         A[i]=max(A[i-1],P+p[i]);
         C[i]=max(C[i-1],P+p[i]+Mx);
         Mx=max(Mx,p[i]-P);
    }
    P=0; Mx=0; ll res=C[tot];
    for(int i=tot;i>=1;i--){
        if(i==tot) P=0;
        else P+=L[i];
        B[i]=max(B[i+1],P+p[i]);
        D[i]=max(D[i+1],P+p[i]+Mx);
        Mx=max(Mx,p[i]-P);
    }
    rep(i,1,tot-1) {
        ll tmp=max(max(C[i],D[i+1]),A[i]+B[i+1]+L[tot]);
        res=min(res,tmp);
    }
    ans=max(ans,res);
}
int main()
{
    int N,u,v,w;
    scanf("%d",&N);
    rep(i,1,N) {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w); add(v,u,w);
    }
    dfs(1,0);
    rep(i,1,tot) treedp(r[i],0);
    solve();
    printf("%lld",ans/2);
    if(ans&1) puts(".5");
    else puts(".0");
    return 0;
}

 

posted @ 2019-09-06 13:41  nimphy  阅读(264)  评论(0编辑  收藏  举报