zoukankan      html  css  js  c++  java
  • [IOI2008]Island

    题目大意:求多棵基环树的直径总和。
    思路:对于基环树系列的题,最重要的就是找环,其他操作都是基于环的。本题也是如此:一棵基环树的直径有两种情况:1、经过环上两个节点外加他们子树的最深深度,也就是dis(i,j)+dep[i]+dep[j],其中dep代表从根节点开始所能到达的最深节点,dist代表i到j的最大距离,有两种情况(顺时针或逆时针,取大的那个)。2、环上某个节点子树的直径。

    首先如何找环?本来可以用dfs随便找,但是毒瘤出题人卡dfs,那怎么办呢?我们发现这个输入特别妙,他每次给出第i个点通向哪里,我们可以就按这样模拟,直到找到曾经访问过的点就找到了环。

    第一种情况:对于处理出来的环,我们要将其破环城链,也就是记录两遍环的节点。然后我们预处理出他们的dep,可以用bfs或dfs求。注意:dep不是直径。对于每个dis(i,j),我们记个前缀和,以环上某个节点为基准,可以列出一个简单式子:ans=Max{(sum[j]-dep[j])+(sum[i]+dep[i])},(因为我们把环拆成两倍,所以涵盖了所有顺时针和逆时针的状态)对这个式子,我们可以枚举i,也就是知道了sum[i]+dep[i]的值,然后我们要求最大的sum[j]-dep[j],这可以用一个单调队列维护。
    第二种情况:对每个环上节点求一遍树的直径,我是用树形dp,设dp[x]是以x为根的最长链,我们对于x的任意两个儿子节点i,j,经过x的最长链就是就过i的最长链加dis(x,i),和过j的最长链加dis(x,j),也就是ans=max(dp[i]+dp[j]+dis(x,i)+dis(x,j))(1<=i<j<son[x])。这看似要双重循环,但我们观察到dp[x]包含了x到dp[i]的最长链,所以我们只要枚举另一个子节点j+dis(x,j)和dis[x]相加更新ans,然后用dp[j]+dis(x,j)更新dis[x]。
    最后一、二种情况取个max就是答案

    注意:这题毒瘤得很,空间开得很小,又卡dfs,因为要破环城链所以要开两倍空间,还有dep不是树的直径,而是最深节点,有些数组要开long long。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    const int Maxn=1e6+7;
    int n,dfn[Maxn],id;
    int last[Maxn],tot,cnt;
    int vis[Maxn],que[2*Maxn],pd[Maxn],f[Maxn],dvi[Maxn];
    long long las,nic,dep[2*Maxn],Q[2*Maxn],dis[Maxn],ans;
    struct edge{
        int to,next,w;
    }e[4*Maxn];
    struct hoop{
        int to,w;
    }fa[Maxn],ring[Maxn];
    inline int read(){
       int s=0,w=1;
       char ch=getchar();
       while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
       while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
       return s*w;
    }
    inline void add_edge(int x,int y,int w){
        e[++tot].to=y;
        e[tot].next=last[x];
        e[tot].w=w;
        last[x]=tot;
        return;
    }
    inline long long K(int x){
        return dep[x]-Q[x];
    }
    inline void find_hoop(int x){ //找树上的环,不能用dfs求
        dvi[x]=1;
        while(1){
            int u=fa[x].to;
            if(dvi[u]){
                ring[++cnt].to=u;
                ring[cnt].w=fa[u].w;dfn[u]=1;
                for(int i=x;i!=u;i=f[i]) dfn[i]=1,ring[++cnt].to=i,ring[cnt].w=fa[i].w;
                break;
            }
            else{
                dvi[u]=1;
                f[u]=x;
            }
            x=u;
        }
        return;
    }
    inline void dp(int x){ //树形dp
        vis[x]=1;dfn[x]=1;
        for(int i=last[x];i;i=e[i].next){
            int u=e[i].to;
            if(vis[u]||pd[u]) continue;
            dp(u);
            nic=max(nic,dis[x]+dis[u]+e[i].w);
            dis[x]=max(dis[x],dis[u]+e[i].w); 
        }
        return;
    }
    void dfs(int x,int f,long long w){ //求环上每个节点的dep值
        if(w>=nic) nic=w;
        for(int i=last[x];i;i=e[i].next){
            int u=e[i].to;
            if(u!=f&&!pd[u]) dfs(u,x,w+e[i].w);
        }
    }
    inline void first(int i){
        nic=0;dfs(ring[i].to,0,0);dep[i]=nic;//!!!
        nic=0;dp(ring[i].to);
        las=max(las,nic);
        return;
    }
    inline void quee(){ //单调队列优化
        int head=1,tail=0;
        for(int i=1;i<=2*cnt;++i){
            if(i<=cnt) Q[i]=Q[i-1]+ring[i].w;
            else Q[i]=Q[i-1]+ring[i-cnt].w;
            if(head<=tail) las=max(las,K(que[head])+Q[i]+dep[i]);
            while(head<=tail&&K(que[tail])<=K(i)) --tail;
            que[++tail]=i;
            while(head<=tail&&que[head]<=i-cnt+1) ++head;
        }
        ans+=las;
        return;
    }
    int main(){
        int y,w;
        n=read();
        for(int i=1;i<=n;++i){
            y=read();w=read();
            add_edge(i,y,w);add_edge(y,i,w);
            fa[i].to=y;fa[i].w=w; //记录方便枚举
        }
        for(int i=1;i<=n;++i){
            if(!dfn[i]){
                cnt=0;id=0;las=0;
                find_hoop(i);
                for(int j=1;j<=cnt;++j) pd[ring[j].to]=1;
                for(int j=1;j<=cnt;++j) first(j);
                for(int j=1;j<=cnt;++j) dep[j+cnt]=dep[j]; //把环断成链 
                quee();
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    ofbiz初级教程
    IBASE4J开发环境搭建
    Nginx在windows上安装 及 Nginx的配置及优化
    Windows里正确安装Zookeeper以服务运行
    分享一个完整的Mybatis分页解决方案
    jquery weui ajax滚动加载更多
    Spring+Mybatis+SpringMVC后台与前台分页展示实例
    Mysql怎么样避免全表扫描,sql查询优化
    SQL优化|Java面试题
    mysql 全表扫描、全索引扫描、索引覆盖(覆盖索引)
  • 原文地址:https://www.cnblogs.com/X-rice/p/10816860.html
Copyright © 2011-2022 走看看