• bzoj1791[IOI2008]Island岛屿(基环树+DP)


    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 
    题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和。n<=1e6

    题解:基环树DP写的很少……

    树的直径不用解释了,就是NOIP2018D1T3的20分做法,基环树直径,我们可以yy一下然后就能发现答案是2种:1、把环剖去以后其余的子树的直径。2、环上的一部分+选中的2点中各自的最长链。

    第一种可以直接dfs求解,对于第二种……我们可以摒弃垃圾的dfs两遍求树的直径的做法,改成DP形式的求树的直径。然后找到一个环时,记录环上的点最深的深度,然后可以把这个环扩展,记录l,r指针,扫描,随便搞一下就能求得答案。

    所以本蒟蒻想到的具体做法就是:先topsort一下,把环给找出来,边topsort边DP,然后遇到环再dfs就OK了

    不说废话看代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=2e6+7;
    int n,cnt,tim,hd[N],v[N],nxt[N],w[N],c[N],du[N],vis[N],q[N];
    ll ans,d[N],f[N],a[N],b[N];
    void add(int x,int y,int z){v[++cnt]=y,nxt[cnt]=hd[x],hd[x]=cnt,w[cnt]=z;}
    void dfs(int u,int k)
    {
        c[u]=k;
        for(int i=hd[u];i;i=nxt[i])if(!c[v[i]])dfs(v[i],k);
    }
    void topsort()
    {
        int qs=0,qe=0;
        for(int i=1;i<=n;i++)if(du[i]==1)q[qe++]=i;
        while(qs<qe)
        {
            int u=q[qs++];
            for(int i=hd[u];i;i=nxt[i])
            if(du[v[i]]>1)
            {
                du[v[i]]--;
                d[c[u]]=max(d[c[u]],f[u]+f[v[i]]+w[i]);
                f[v[i]]=max(f[v[i]],f[u]+w[i]);
                if(du[v[i]]==1)q[qe++]=v[i];
            }
        }
    }
    void dp(int x,int tp)
    {
        int m=0,y=x,i;
        do{
            a[++m]=f[y];
            du[y]=1;
            for(i=hd[y];i;i=nxt[i])
            if(du[v[i]]>1){b[m+1]=b[m]+w[i];y=v[i];break;}
        }while(i);
        if(m==2)
        {
            int len=0;
            for(int i=hd[y];i;i=nxt[i])if(v[i]==x)len=max(len,w[i]);
            d[tp]=max(d[tp],f[x]+f[y]+len);
            return;
        }
        for(int i=hd[y];i;i=nxt[i])if(v[i]==x){b[m+1]=b[m]+w[i];break;}
        for(int i=1;i<=m;i++)a[m+i]=a[i],b[m+i]=b[m+1]+b[i];
        int l=1,r=1;
        l=r=q[1]=1;
        for(int i=2;i<2*m;i++)
        {
            while(l<=r&&i-q[l]>=m)l++;
            d[tp]=max(d[tp],b[i]-b[q[l]]+a[i]+a[q[l]]);
            while(l<=r&&a[q[r]]+b[i]-b[q[r]]<=a[i])r--;
            q[++r]=i;
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1,x,y;i<=n;i++)scanf("%d%d",&x,&y),add(i,x,y),add(x,i,y),du[x]++,du[i]++;
        for(int i=1;i<=n;i++)if(!c[i])dfs(i,++tim);
        topsort();
        for(int i=1;i<=n;i++)
        if(du[i]>1&&!vis[c[i]])vis[c[i]]=1,dp(i,c[i]),ans+=d[c[i]];
        printf("%lld",ans);
    }
    View Code
  • 相关阅读:
    【转】VS2010中 C++创建DLL图解
    [转]error: 'retainCount' is unavailable: not available in automatic reference counting mode
    [转]关于NSAutoreleasePool' is unavailable: not available in automatic reference counting mode的解决方法
    【转】 Tomcat v7.0 Server at localhost was unable to start within 45
    【转】Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If
    【转】SVN管理多个项目版本库
    【转】eclipse安装SVN插件的两种方法
    【转】MYSQL启用日志,和查看日志
    【转】Repository has not been enabled to accept revision propchanges
    【转】SVN库的迁移
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10159396.html
Copyright © 2020-2023  润新知