• [题解][IOI2008]岛屿Island(基环树+dp


    调了一万年......先是==写成=,然后快读出锅,手写队列以为出锅改成stl结果发现stl才是真正出锅....改回手写队列才a

    抄题解都抄了好久好久...(写法不唯一...

    然后BZOJ RE.........WDNMD

    思路,找环,子树求直径,单调队列dp,比较好理解,但是写起来有点麻烦

    这里用dfs找环,传bool型返回值,dfs时因为有重边不能直接记录fa,要按边记录,过程中把环上的点塞到数组里,记录每个点的下标和边权前缀和,并注意记录环的头尾衔接点

    子树求直径用全局标记数组v2,环上的点在找环的时候已经在v2标记过了不用考虑,然后正常树形dp

    单调队列dp,因为数组不清空,所以每次记录一下当前子树对应的下标起始点为st,dp的时候减一下偏移量

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define ll long long
    using namespace std;
    const int maxn=1000009;
    inline int read(){
        int ret=0,fix=1;char ch;
        while(!isdigit(ch=getchar()))fix=ch=='-'?-1:fix;
        do ret=(ret<<1)+(ret<<3)+ch-'0';
        while(isdigit(ch=getchar()));
        return fix*ret;
    }
    int n,st;//st为本子树中环开始编号 
    struct node{
        int v,nxt,w;
    }e[maxn<<1];
    int head[maxn],cnt=1;
    inline void add(int u,int v,int w){
        e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].w=w;
        e[++cnt].v=u;e[cnt].nxt=head[v];head[v]=cnt;e[cnt].w=w;
    }
    //找环 
    int v[maxn],v2[maxn],id[maxn],tot;
    ll s[maxn<<1];
    //v=1走过,v=2环的头尾衔接点 
    //v2=1所在基环树走过, 
    //id原始编号 
    //s为边长度前缀和,dp用 
    bool dfs(int x,int last){
        if(v[x]==1){
            v[x]=2;id[++tot]=x,v2[x]=1;
            return 1;
        }
        v[x]=1;
        for(int i=head[x];i;i=e[i].nxt){
            if(i!=(last^1)&&dfs(e[i].v,i)){
                if(v[x]!=2)id[++tot]=x,v2[x]=1,s[tot]=s[tot-1]+e[i].w;
                else{
                    s[st-1]=s[st]-e[i].w;
                    return 0;
                }
                return 1;
            }
        }
        return 0;
    }
    //子树直径 
    ll d[maxn],ans,ans2;
    void subdp(int x){
        v2[x]=1;
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].v;
            if(v2[y])continue;
            subdp(y);
            ans=max(ans,d[x]+d[y]+e[i].w);
            d[x]=max(d[x],d[y]+e[i].w);
        }
    }
    //带环直径 
    ll f[maxn<<1],ans3;
    int q[maxn<<1],hd=1,tl;
    void dp(){
        hd=1,tl=0;
        for(int i=st;i<=tot;i++){//环复制成链 
            f[i+tot-st+1]=f[i]=d[id[i]];
            s[i+tot-st+1]=s[i+tot-st]+s[i]-s[i-1];
        }
        for(int i=st;i<=tot*2-st+1;i++){
            while(hd<=tl && q[hd]<=i-tot+st-1)hd++;
            if(hd<=tl)ans3=max(ans3,f[i]+f[q[hd]]+s[i]-s[q[hd]]);
            while(hd<=tl && f[q[tl]]-s[q[tl]]<=f[i]-s[i])tl--;
            q[++tl]=i;
        }
    //    deque<int>q;
    //    for(int i=st;i<=tot*2-st+1;i++){
    //        while(!q.empty() && q.front()<=i-tot+st-1)q.pop_front();
    //        if(!q.empty())ans3=max(ans3,f[i]+f[q.front()]+s[i]-s[q.front()]);
    //        while(!q.empty() && f[q.front()]-s[q.front()]<=f[i]-s[i])q.pop_back();
    //        q.push_back(i);
    //    }
    }
    ll work(int x){
        st=tot+1,ans2=0,ans3=0;
        //找环 
        dfs(x,0);
        //子树直径 
        for(int i=st;i<=tot;i++){
            ans=0;
            subdp(id[i]);
            ans2=max(ans2,ans);
    //        f[i+tot-st+1]=f[i]=d[id[i]];
    //        s[i+tot-st+1]=s[i+tot-st]+s[i]-s[i-1];
        }
        dp();
        return max(ans2,ans3);
    }
    int main(){
        scanf("%d",&n);
    //    for(int i=1;i<=n;i++)add(i,read(),read());
        for(int i=1,u,v;i<=n;i++){
            scanf("%d%d",&u,&v);
            add(i,u,v);
        }
        ll anss=0;
        for(int i=1;i<=n;i++)
        if(!v2[i])anss+=work(i);
        printf("%lld",anss);
    //    for(int i=1;i<=n;i++)printf("%d ",d[i]);
    }
  • 相关阅读:
    来了!GitHub for mobile 发布!iOS beta 版已来,Android 版即将发布
    五角场之殇。曾与张江、漕河泾、紫竹齐名。如今,上海四大IT科技园是否还在?
    Visual Studio Online 的 FAQ:iPad 支持、自托管环境、Web 版 VS Code、Azure 账号等
    VS Code 1.40 发布!可自行搭建 Web 版 VS Code!
    Visual Studio Online,带来四种开发模式,未来已来。
    基于七牛云对象存储,搭建一个自己专属的极简Web图床应用(手摸手的注释讲解核心部分的实现原理)
    css伪元素::before与::after使用基础示例
    gcc生成静态链接库与动态链接库步骤,并链接生成可执行文件的简单示例
    shell求水仙花数
    shell实现简单的数组排序
  • 原文地址:https://www.cnblogs.com/superminivan/p/11459406.html
Copyright © 2020-2023  润新知