• 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
  • 相关阅读:
    pstree---树状图的方式展现进程
    telint---切换当前正在运行的Linux系统的运行等级
    batch---系统不繁忙时执行任务
    crontab---定时任务
    runlevel---当前Linux系统的运行等级
    kill&&pkill&&killall---删除执行中的程序
    at&&atq&&atrm---定时任务
    fsck---于检查并且试图修复文件系统中的错误
    mkfs---创建Linux文件系统
    gitignore
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10159396.html
Copyright © 2020-2023  润新知