岛屿(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;
}

原文地址:https://www.cnblogs.com/lmjer/p/9363946.html

时间: 2024-10-09 15:21:54

岛屿(bzoj1791)的相关文章

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

bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_x$ 之后记录环上边长前缀和$ns_i$ dp值为$max_{i,j}dp[i]+sum[i]+dp[j]-sum[j]$ $dp[j]-sum[j]$提出来进单调队列. O(n). 记得dfs改bfs. #include<cstdio> #include<algorithm> using namespace std; typedef long long lin

东方14模拟赛之岛屿

02:岛屿 查看 提交 统计 提问 总时间限制:  40000ms 单个测试点时间限制:  4000ms 内存限制:  128000kB 描述 从前有一座岛屿,这座岛屿是一个长方形,被划为N*M的方格区域,每个区域都有一个确定的高度.不幸的是海平面开始上涨,在第i年,海平面的高度为t[i].如果一个区域的高度小于等于海平面高度,则视为被淹没.那些没有被淹没的连通的区域够成一个连通块.现在问第i年,这样的连通块有多少个. 例如:第一年海平面高度为1,有2个连通块. 第二年海平面高度为2,有3个连通

51nod 1276 1276 岛屿的数量 (很好玩的题目

题意: 有N个岛连在一起形成了一个大的岛屿,如果海平面上升超过某些岛的高度时,则这个岛会被淹没.原本的大岛屿则会分为多个小岛屿,如果海平面一直上升,则所有岛都会被淹没在水下. 给出N个岛的高度.然后有Q个查询,每个查询给出一个海平面的高度H,问当海平面高度达到H时,海上共有多少个岛屿.例如: 岛屿的高度为:{2, 1, 3, 2, 3}, 查询为:{0, 1, 3, 2}. 当海面高度为0时,所有的岛形成了1个岛屿. 当海面高度为1时,岛1会被淹没,总共有2个岛屿{2} {3, 2, 3}. 当

nyoj 1237 最大岛屿(dfs)

描述 神秘的海洋,惊险的探险之路,打捞海底宝藏,激烈的海战,海盗劫富等等.加勒比海盗,你知道吧?杰克船长驾驶着自己的的战船黑珍珠1号要征服各个海岛的海盜,最后成为海盗王. 这是一个由海洋.岛屿和海盗组成的危险世界.面对危险重重的海洋与诡谲的对手,如何凭借智慧与运气,建立起一个强大的海盗帝国. 杰克船长手头有一张整个海域的海图,上面密密麻麻分布着各个海屿的位置及面积.他想尽快知道整个海域共有多少岛屿以及最大岛屿的面积. 输入 第1行:M N T,表示海域的长,宽及一个单位表示的面积大小 接下来有M

岛屿的个数

|题目 给一个01矩阵,求不同的岛屿的个数. 0代表海,1代表岛,如果两个1相邻,那么这两个1属于同一个岛.我们只考虑上下左右为相邻. |在线测试本题 http://www.lintcode.com/zh-cn/problem/number-of-islands/ |难度 容易

Poj1328 用雷达覆盖所有的岛屿

(此配图来自http://blog.csdn.net/zhengnanlee/article/details/9613161) 图中ABCD为海岛的位置.题目中会给出几个海岛的坐标位置,雷达覆盖半径d,问你用几个雷达可实现海岛的全部覆盖,不能就输出-1. 观察图可知:(ABCD已经按照左交点数的大小好位置) A的右交点>B的左交点,所以AB可共用一个雷达: B的右交点>C的左交点,所以BC可共用一个雷达: C的右交点<D的左交点,所以CD不可共用一个雷达: 所以一共用2个雷达即可实现岛屿

岛屿(洛谷 U5399)

题目背景 放假了,Lkw和mm到岛上旅游.阳光明媚,风景秀丽.正当Lkw和mm享受眼前这旖旎的风光时,突降大雨,小岛上开始积水,没过几分钟,水便快要触及膝盖.Lkw和mm意识到了事态的严重性,赶紧向高地跑去,可在涌动的人流中,Lkw和mm失散了...水越涨越高,Lkw拿着望远镜四处寻找,耳边不停地传来mm的呼喊,可就是不见mm的身影--焦急的Lkw想知道mm可能在几个区域,你能帮助他么? 题目描述 从水平方向看,我们把岛屿分成n个小块,每个部分用一个数h表示高度,每个区域由相连的小块组成.一开始

nyoj1237 最大岛屿(河南省第八届acm程序设计大赛)

题目1237 题目信息 运行结果 本题排行 讨论区 最大岛屿 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描述 神秘的海洋,惊险的探险之路,打捞海底宝藏,激烈的海战,海盗劫富等等.加勒比海盗,你知道吧?杰克船长驾驶着自己的的战船黑珍珠1号要征服各个海岛的海盜,最后成为海盗王.  这是一个由海洋.岛屿和海盗组成的危险世界.面对危险重重的海洋与诡谲的对手,如何凭借智慧与运气,建立起一个强大的海盗帝国. 杰克船长手头有一张整个海域的海图,上面密密麻麻分布着各个海屿的位置及面

最大岛屿

最大岛屿时间限制: 1000ms内存限制: 128000KB64位整型: Java 类名: 上一题 提交 运行结果 统计 讨论版 下一题 类型: 没有 没有 难度 lv.1 lv.2 lv.3 lv.4 lv.5 lv.6 lv.7 lv.8 lv.9 lv.10 搜索 数据结构 动态规划 STL练习 高精度计算 图论 几何 数学 矩阵计算 入门题目 字符串 博弈论添加 题目描述 神秘的海洋,惊险的探险之路,打捞海底宝藏,激烈的海战,海盗劫富等等.加勒比海盗,你知道吧?杰克船长驾驶着自己的的战船