樹形dp 基環樹直徑 bzoj1791 ioi2008island

這個題真的好麻煩啊。。。

就是給一堆基環樹然後求出他們的直徑的和


我們首先不考慮環,然後對於每個點求出他所能走到的最大深度,

然後縮點,就成了一個麪包圈一樣的環了


然後脫環成鏈直接用單調隊列dp就可以了


隊列中維護dp的決策,由於決策是有單調性的。。。

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<algorithm>
#define MAX 1000010
#define ll long long
#define max(a,b) a>b?a:b
#define rep(i,j,k) for(int i=j;i<=k;i++)
 
using namespace std;
 
int next[MAX*2],head[MAX],to[2*MAX],n,tot=0;
int scc_num=0,value[MAX*2];
int du[MAX]={0};ll d[MAX];
int vis[MAX]={0};
int q[MAX*2];
ll ans=0,f[MAX],a[MAX*2],b[2*MAX];
int done[MAX],scc[MAX]={0};
 
void add(int from,int To,int weight)
{
    du[To]++;
    tot++;
    to[tot]=To;
    next[tot]=head[from];
    value[tot]=weight;
    head[from]=tot;
    return;
}
 
void bfs(int x,int number)
{
    queue<int>q;
    q.push(x);
    scc[x]=number;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=head[now];i!=-1;i=next[i])
            if(!scc[to[i]])
                scc[to[i]]=number,q.push(to[i]);
    }
    return;
}

void topsort()
{
    queue<int>q;
    rep(i,1,n)
        if(du[i]==1)
            q.push(i);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=head[now];i!=-1;i=next[i])
            if(du[to[i]]>1)
            {
                d[scc[now]]=max(d[scc[now]],f[now]+f[to[i]]+value[i]);
                f[to[i]]=max(f[to[i]],f[now]+value[i]);
                if((--du[to[i]])==1)
                    q.push(to[i]);
            }
    }
    return;
}

void dp(int sc,int x)
{
    int y=x,p,t=0,l=0,r;
    do
    {
        a[++t]=f[y];
        du[y]=1;
        for(p=head[y];p!=-1;p=next[p])
            if(du[to[p]]>1)
            {
                y=to[p];
                b[t+1]=b[t]+value[p];
                break;
            }
    }while(p!=-1);
 
    if(t==2)
    {
        for(int i=head[y];i!=-1;i=next[i])
            if(to[i]==x)
                l=max(l,value[i]);
        d[sc]=max(d[sc],f[x]+f[y]+l);
        return;
    }
 
    for(int i=head[y];i!=-1;i=next[i])
        if(to[i]==x)
        {
            b[t+1]=b[t]+value[i];
            break;
        }
    rep(i,1,t-1)
        a[t+i]=a[i],b[t+i]=b[t+1]+b[i];
    r=1;
    q[l=1]=1;
    for(int i=2;i<2*t;i++)
    {
        while(l<=r&&i-q[l]>=t)
            l++;
        d[sc]=max(d[sc],a[i]+a[q[l]]+b[i]-b[q[l]]);
        while(l<=r&&a[q[r]]+b[i]-b[q[r]]<=a[i])
            r--;
        q[++r]=i;
    }
    return;
}
 
int main()
{
    memset(next,-1,sizeof(next));
    scanf("%d",&n);
    for(int i=1,a1,a2;i<=n;i++)
        scanf("%d%d",&a1,&a2),add(i,a1,a2),add(a1,i,a2);
    scc_num=0;
    rep(i,1,n)
        if(!scc[i])
            bfs(i,++scc_num);
    topsort();
    rep(i,1,n)
        if(du[i]>1&&!vis[scc[i]])
        {
            vis[scc[i]]=1;
            dp(scc[i],i);
            ans+=d[scc[i]];
        }
	cout<<ans<<endl;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章