其他分享
首页 > 其他分享> > P4381 [IOI2008] Island

P4381 [IOI2008] Island

作者:互联网

【题意】

给一个基环树森林,求每个基环树的最长链之和

【分析】

对于每个基环树,我们可以把环先当成一个点看待,然后作为树的根节点

这时,直径有两种情况:

1.完全在根的一个子树内,不经过根

2.跨过根,位于两个子树内

 

可以先dfs一波找到环,然后计算第一种情况,对于根的每个子树进行以此树形dp,取max就是第一种的答案

然后把每个点子树的结果记录到环对应的点上,这时我们就是要计算第二种情况

考虑我们可以取环上的一段+两个端点的子树的结果(刚刚计算出来的部分)

显然暴力取枚举是$O(n^2)$的,我们考虑断环为链,接上长度为2倍的部分,然后在上面利用单调队列计算

 

这个dfs找环的时候稍微注意一下就好了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,tot,l,que[maxn],tim,id[maxn],vis[maxn],fa[maxn],mark[maxn],times,inin[maxn],dep[maxn],head[maxn];
long long b[maxn],maxdp,dp[maxn],a[maxn];
vector <int> cir[maxn];
struct edge
{
    int to,nxt,v;
}e[maxn<<1];
void add(int x,int y,int z)
{
    e[++tot].nxt=head[x];
    e[tot].to=y;
    head[x]=tot;
    e[tot].v=z;
    inin[y]++;
}
void dfs(int x,int deep,int laste,int from)
{
    fa[x]=from; dep[x]=deep;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(!dep[to]) dfs(to,deep+1,i,x);
        else if((laste!=i-1 && laste!=i+1) && dep[to]<dep[x])
        {
            ++times;
            for(int now=x;now!=to;now=fa[now])
                cir[times].push_back(now);
            cir[times].push_back(to);
        }
    }
}
void getw(int x,long long d,int laste)
{
    vis[x]++; id[++tim]=x; b[tim]=d;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(laste!=i-1&& laste!=i+1 && vis[to]<2 && mark[to])
            getw(to,d+e[i].v,i);
    }
}
void DP(int x,int fa)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || mark[to] ) continue;
        DP(to,x);
        maxdp=max(maxdp,dp[x]+dp[to]+e[i].v);
        dp[x]=max(dp[x],dp[to]+e[i].v);
    }
}
int q[maxn];
long long solve(int x)
{
    long long res=0;
    for(int k=0;k<cir[x].size();k++) mark[cir[x][k]]=1;
    tim=0,getw(cir[x][0],0,0);
    for(int i=1;i<=cir[x].size();i++)
    {
        int x=id[i]; 
        maxdp=0,DP(x,0),res=max(res,maxdp);
        a[i]=dp[x];
    }
    int len=cir[x].size();
    for(int i=1;i<=cir[x].size();i++) 
        a[i+len]=a[i];
    int l=1,r=1; q[l]=1;
    for (int i=2;i<=tim;i++){
        while (l<=r&&i-q[l]>=len)l++;
        int j=q[l]; if (l<=r)res=max(res,a[i]+a[j]+b[i]-b[j]);
        while (l<=r&&a[i]-b[i]>a[q[r]]-b[q[r]])r--;
        q[++r]=i;
    }
    return res;
}
int main()
{
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        add(i,x,y); add(x,i,y);    
    }    
    for(int i=1;i<=n;i++)
        if(!dep[i])
            dfs(i,1,0,0);
    long long ans=0;
    for(int i=1;i<=times;i++)
        ans+=solve(i);
    printf("%lld",ans);
    return 0;
}

 

标签:IOI2008,子树内,int,Island,基环树,maxn,freopen,P4381,dp
来源: https://www.cnblogs.com/andylnx/p/14968781.html