bzoj3242: [Noi2013]快餐店

版权声明:转载请注明出处 https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81678396

链接

https://www.lydsy.com/JudgeOnline/problem.php?id=3242

这题求啥

如果是一棵树,直接求直径
这个题的题意可以这样叙述:给你一个基环树,用 d i s t ( i , j ) 表示 i , j 间的最短路,求一个点对 ( i , j ) 使得 d i s t ( i , j ) 最大并且输出 d i s t ( i , j ) 2

怎么找环

直接 d f s ,当我发现要去的那个点已经带有标记时,这条边就在环上,这个点也在环上,记录 d f s 树的前驱,从当前点一直回溯回去一路经过的点就是环上的点

分析问题

可能树上有一条链就是最长的,那就直接去那个长度
对于每个环上的点,我直接在这棵外向树中 d p ,计算出 f [ i ] 表示以节点 i 为一个端点,另一个端点在树中的最长路径长度,同时统计最长链更新 a n s
问题是还有一种从一棵子树出来经过环的一段然后进入另一棵子树的路径
显然这个路径不可能经过环上的每一条边
于是就有了这样的想法:直接枚举砍断哪条边,然后 O ( N ) 做DP,总时间复杂度 O ( N 2 )
能不能快速维护 D P 的结果?
假设环上的点是 1... M
假设我砍断 1 M 的连边,那么 a n s = m a x { f [ i ] + s [ i ] + f [ j ] s [ j ] } ( j < i )
其中 s 是环上边权的前缀和
这个东西有人用线段树维护,我用了个堆就解决了,一个堆维护 f [ i ] + s [ i ] ,另一个堆维护 f [ i ] s [ i ] ,堆中的元素记录序号和权值,每次从两个堆都取堆顶,如果序号相等,我再取一次堆顶(相当于取了次大值),然后瞎搞一搞就行了
有人会觉得:不是应该 j < i 吗?你这样怎么满足?
显然,当 ( i , j ) i < j 时,s的差是负数,肯定不如 ( j , i ) 更优,因此我在做决策的时候其实已经把它舍掉了
我把 f s 都复制粘贴在尾端,然后用一个长度为 N 的窗口滑动一下,窗口与窗口之间取 m i n ,这样不就做完了?

代码

//环套树、堆
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <queue>
#define cl(x,y) memset(x,y,sizeof(x))
#define ll_inf (1ll<<60)
#define ll long long
#define maxn 100010
#define heap priority_queue
using namespace std;
ll head[maxn], nex[maxn<<1], to[maxn<<1], w[maxn<<1], tot=1, list[maxn<<1], len[maxn<<1], f[maxn], pre[maxn], N, vis[maxn<<1], mark[maxn], 
    v1[maxn<<1], v2[maxn<<1], ans;
ll read(ll x=0)
{
    char c, f=1;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
    return f*x;
}
void adde(ll a, ll b, ll v){to[++tot]=b;nex[tot]=head[a];head[a]=tot;w[tot]=v;}
void dfs1(ll pos)
{
    ll p, x;
    vis[pos]=1;
    for(p=head[pos];p;p=nex[p])
        if(p!=pre[pos])
        {
            if(!vis[to[p]])pre[to[p]]=p^1, dfs1(to[p]);
            else
            {
                pre[to[p]]=p^1;
                list[++*list]=to[p], len[*list]=w[p], mark[to[p]]=1;
                for(x=pos;x!=list[1];x=to[pre[x]])list[++*list]=x, len[*list]=w[pre[x]], mark[x]=1;
            }
        }
}
void dfs2(ll pos)
{
    ll p, mx1=-ll_inf, mx2=-ll_inf, t;
    vis[pos]=1;
    for(p=head[pos];p;p=nex[p])
        if(!vis[to[p]] and !mark[to[p]])
        {
            dfs2(to[p]);
            t=w[p]+f[to[p]];
            f[pos]=max(f[pos],t);
            if(t>=mx1)mx2=mx1, mx1=t;
            else if(t>mx2)mx2=t;
        }
    ans=max(ans,mx1+mx2);
}
void init()
{
    ll i, a, b, l;
    N=read();
    for(i=1;i<=N;i++)a=read(), b=read(), l=read(), adde(a,b,l), adde(b,a,l);
    dfs1(1);
    cl(vis,0);
    for(i=1;i<=*list;i++)dfs2(list[i]);
}
struct root
{
    ll val, id;
    root(){}
    root(ll a, ll b){val=a,id=b;}
};
bool operator<(root a, root b){return a.val<b.val;}
root get(heap<root> &h, ll l, ll r)
{
    root x=h.top();h.pop();
    while(x.id<l or x.id>r)x=h.top(), h.pop();
    return x;
}
void solve()
{
    ll i, l, r, t=ll_inf, tmp, M=*list;
    if(M<=1)return;
    heap<root> h1, h2;
    root a, b, c, d;
    for(i=1;i<=M;i++)len[M+i]=len[i], list[M+i]=list[i];
    for(i=1;i<=M<<1;i++)len[i]+=len[i-1];
    for(i=1;i<=M<<1;i++)v1[i]=-len[i-1]+f[list[i]];
    for(i=1;i<=M<<1;i++)v2[i]=len[i-1]+f[list[i]];
    for(i=1;i<M;i++)h1.push(root(v1[i],i)), h2.push(root(v2[i],i));
    for(i=1;i<=M;i++)
    {
        l=i, r=i+M-1;
        h1.push(root(v1[r],r));
        h2.push(root(v2[r],r));
        a=get(h1,l,r), b=get(h1,l,r), c=get(h2,l,r), d=get(h2,l,r);
        h1.push(a), h1.push(b), h2.push(c), h2.push(d);
        if(a.id!=c.id)tmp=a.val+c.val;
        else tmp=max(a.val+d.val,b.val+c.val);
        t=min(t,tmp);
    }
    ans=max(ans,t);
}
int main()
{
    init();
    solve();
    printf("%lld.%d",ans>>1,ans&1?5:0);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81678396