岛屿(bzoj1791)

Posted lmjer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了岛屿(bzoj1791)相关的知识,希望对你有一定的参考价值。

1791: [Ioi2008]Island 岛屿

Time Limit: 20 Sec  Memory Limit: 162 MB
Submit: 2042  Solved: 461
[Submit][Status][Discuss]

Description

你将要游览一个有N个岛屿的公园。从每一个岛i出发,只建造一座桥。桥的长度以Li表示。公园内总共有N座桥。尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走。同时,每一对这样的岛屿,都有一艘专用的往来两岛之间的渡船。 相对于乘船而言,你更喜欢步行。你希望所经过的桥的总长度尽可能的长,但受到以下的限制。 ? 可以自行挑选一个岛开始游览。 ? 任何一个岛都不能游览一次以上。 ? 无论任何时间你都可以由你现在所在的岛S去另一个你从未到过的岛D。由S到D可以有以下方法: o 步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离;或者 o 渡船:你可以选择这种方法,仅当没有任何桥和/或以前使用过的渡船的组合可以由S走到D(当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。 注意,你不必游览所有的岛,也可能无法走完所有的桥。 任务 编写一个程序,给定N座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的最大长度。 限制 2 <= N <= 1,000,000 公园内的岛屿数目。 1<= Li <= 100,000,000 桥i的长度。

Input

? 第一行包含N个整数,即公园内岛屿的数目。岛屿由1到N编号。 ? 随后的N行每一行用来表示一个岛。第i 行由两个以单空格分隔的整数,表示由岛i筑的桥。第一个整数表示桥另一端的岛,第二个整数表示该桥的长度Li。你可以假设对於每座桥,其端点总是位于不同的岛上。

Output

你的程序必须向标准输出写出包含一个整数的单一行,即可能的最大步行距离。 注1:对某些测试,答案可能无法放进32-bit整数,你要取得这道题的满分,可能需要用Pascal的int64或C/C++的long long类型。 注2:在比赛环境运行Pascal程序,由标准输入读入64-bit数据比32-bit数据要慢得多,即使被读取的数据可以32-bit表示。我们建议把输入数据读入到32-bit数据类型。 评分 N不会超过4,000。

Sample Input

7
3 8
7 2
4 2
1 4
1 9
3 4
2 3


Sample Output

24

HINT

技术分享图片

这道题可以学到很多东西

首先先以环中每个点为根求出最远能到达的距离

然后最长链为环中某两点的距离再加上,这两点最远到达的距离

然后具体怎么求这些东西呢

首先求以环中每个点为根求出最远能到达的距离

我们先标记每个点的入度,若一开始入度为一,则说明此点为叶子节点,然后通过不断向上求,并且不断删掉该点的叶子节点个数,就可以求到环上的点

而最后环上的点必定入度为二,所以可以实现避免环上的点求最远距离,并且可以找出环来


void topsort(){
     ll l=1,r=0;
     for (ll i=1;i<=n;i++) if(du[i]==1) Q[++r]=i;
     while(l<=r){
        ll u=Q[l];
        for (ll i=adj[u];i;i=bian[i].next){
            ll v=bian[i].v;
            if(du[v]>1){
                d[c[u]]=max(d[c[u]],f[v]+f[u]+bian[i].w);
                f[v]=max(f[v],f[u]+bian[i].w);
                if((--du[v])==1) Q[++r]=v;
            }
        }
        l++;
     }
}

最后求环上的最远距离可以把环拆开复制成两倍做成一条链,便可以用单调队列方法求最值

void getans(ll t,ll x){
     ll y=x;
     ll i,r,l=0;
     ll m=0;
     do{
        a[++m]=f[y];
        du[y]=1;
        for (i=adj[y];i;i=bian[i].next){
            ll v=bian[i].v;
            if(du[v]>1){
                b[m+1]=b[m]+bian[i].w;
                y=v;
                break;
            }
        }
     }while(i);
     if(m==2){void getans(ll t,ll x){
     ll y=x;
     ll i,r,l=0;
     ll m=0;
     do{
        a[++m]=f[y];
        du[y]=1;
        for (i=adj[y];i;i=bian[i].next){
            ll v=bian[i].v;
            if(du[v]>1){
                b[m+1]=b[m]+bian[i].w;
                y=v;
                break;
            }
        for (i=adj[y];i;i=bian[i].next){
            if(bian[i].v==x) {b[2]=max(b[2],bian[i].w);}
        }
        d[t]=max(d[t],f[x]+f[y]+b[2]);
        return ;
     }
     for (i=adj[y];i;i=bian[i].next){
        if(bian[i].v==x){
            b[m+1]=b[m]+bian[i].w;
            break;
        }
     }
     for (i=1;i<m;i++) {a[m+i]=a[i];b[m+i]=b[m+1]+b[i];}
     Q[l=r=1]=1;
     for (i=2;i<2*m;i++){
         while(l<=r&&i-Q[l]>=m) l++;
         d[t]=max(d[t],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;
     }
}

 

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn=1000000+10;
typedef long long ll;

struct my{
       int v;
       int next;
       ll w;
};

my bian[maxn*2];
int adj[maxn],n,c[maxn],du[maxn],Q[maxn*2];
bool vis[maxn];
ll f[maxn],fa,d[maxn],a[maxn*2],b[maxn*2];

void myinsert(ll u,ll v,ll w){
     bian[++fa].v=v;
     bian[fa].next=adj[u];
     bian[fa].w=w;
     adj[u]=fa;
     du[v]++;
}

void dfs(ll x,ll t){
     c[x]=t;
     for (ll i=adj[x];i;i=bian[i].next){
            ll v=bian[i].v;
        if(!c[v]){
            dfs(v,t);
        }
     }
}

void topsort(){
     ll l=1,r=0;
     for (ll i=1;i<=n;i++) if(du[i]==1) Q[++r]=i;
     while(l<=r){
        ll u=Q[l];
        for (ll i=adj[u];i;i=bian[i].next){
            ll v=bian[i].v;
            if(du[v]>1){
                d[c[u]]=max(d[c[u]],f[v]+f[u]+bian[i].w);
                f[v]=max(f[v],f[u]+bian[i].w);
                if((--du[v])==1) Q[++r]=v;
            }
        }
        l++;
     }
}

void getans(ll t,ll x){
     ll y=x;
     ll i,r,l=0;
     ll m=0;
     do{
        a[++m]=f[y];
        du[y]=1;
        for (i=adj[y];i;i=bian[i].next){
            ll v=bian[i].v;
            if(du[v]>1){
                b[m+1]=b[m]+bian[i].w;
                y=v;
                break;
            }
        }//把环拆成链
     }while(i);
     if(m==2){
        for (i=adj[y];i;i=bian[i].next){
            if(bian[i].v==x) {b[2]=max(b[2],bian[i].w);}
        }
        d[t]=max(d[t],f[x]+f[y]+b[2]);
        return ;
     }
     for (i=adj[y];i;i=bian[i].next){
        if(bian[i].v==x){
            b[m+1]=b[m]+bian[i].w;
            break;把环的开头找到、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
        }
     }
     for (i=1;i<m;i++) {a[m+i]=a[i];b[m+i]=b[m+1]+b[i];}
     Q[l=r=1]=1;
     for (i=2;i<2*m;i++){
         while(l<=r&&i-Q[l]>=m) l++;
         d[t]=max(d[t],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;
     }
}

int main(){
   // freopen("bzoj1791.in","r",stdin);
    //freopen("bzoj1791.out","w",stdout);
    scanf("%lld",&n);
    ll u,w;
    for (int i=1;i<=n;i++){
        scanf("%lld%lld",&u,&w);
        myinsert(u,i,w);
        myinsert(i,u,w);
    }
    ll t=0;
    for (ll i=1;i<=n;i++)
        if(!c[i]) {
                ++t;
                dfs(i,t);//标记每棵树
        }
    topsort();
    ll ans=0;
    for(ll i=1;i<=n;i++){
        if(du[i]>1&&!vis[c[i]]){
            vis[c[i]]=1;
            getans(c[i],i);
            ans+=d[c[i]];
        }
    }
    printf("%lld ",ans);
return 0;
}

 

 

 
















































































以上是关于岛屿(bzoj1791)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1791 岛屿(环套树+单调队列DP)

[bzoj1791][ioi2008]Island 岛屿(基环树树的直径)

bzoj 1791: [Ioi2008]Island 岛屿基环树+单调队列优化dp

bzoj 合集 1079 1791 1876 2208 2306

bzoj1137POI2009Wsp 岛屿

BZOJ 1137: [POI2009]Wsp 岛屿 半平面交