1791: [Ioi2008]Island 岛屿 

Time Limit: 20 Sec  Memory Limit: 162 MB
Submit: 908  Solved: 159
[Submit][Status]

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

/******************************************************************************/

题意:

输入n,然后n行第i行是由i向外引一条无向边,以及输入边的权值,这样会组成k个联通图,每个图上取两点,使其间路径长度最大,求这些“最大路径”长度总和。

题目分析:

首先一个n点联通图上会有n条边,而它又联通,所有它一定是有环的,而因为有n-1条边的n点联通图保证图联通且无环,剩下一条边则一定使“树”中有且只有一个环,于是得证每个联通图都是一个“基环树”,即有且仅有一个环的树。

那么现在我们应该输出的就是所有基环树的直径总和(树的直径即树上点不重复的最长路径)。

如图,这棵基环树的直径就是11到5,经过3,长度71。

题解:

我们需要对每个基环树单独处理,先搜到它的环,然后dfs求每个环上点往外枝杈的最长路径,这样每个点就有了一个权值,然后环上每个边又有了一个权值,就可以在环上做一发单调队列动规,求基环树直径长度啦!

当然,我们对于每棵树的直径是在动规里面定论的,不过它的初值却不能在动规前赋成0!

如下图中,直径就不经过环,为13到6,长度79,所以在dfs求环上点权的,也不能忘了更新直径值,不过怎么更新就需要略加斟酌了。

实现:

首先双向建边,然后对任意点搜索,搜到某个点已经搜过了,就代表这个点是环上的,然后处理环,再然后就是搜索找到每个点,然后递归出枝杈边权和最长值,作为点权值,进行单调队列优化后的动规,过程中更新直径长度,单调队列把点倍增,然后两点间长度小于环长度即可。

我在此证明一下动规的单调性。【甚水,可略过】首先设点A、B、C、D依次排列,则从A到D(有向,不能从D到A)的长度为环上A到D的长度和加上A、D点权和。不要问我为什么不能从D到A,因为我已经把点倍增了,从D到第二个A就是“从D到A”了。

好吧,跑题了,我来总结一下,

长度A到D = A点权+A~B边长+B~C边长+C~D边长+D点权

那么长度B到D就 = B点权+B~C边长+C~D边长+D点权

发生了什么?什么?只要B点权 > A点权+A~B边长,那么从B到D就比A到D优!同理,后面的一样。

结果出来了,然后输出即可,注意输入要用int,输出用long long,不然会超时,超很多,IOI2008测试数据中isl18f.in秒出解,但是用long long 读入,呵呵呵呵。

时间丶代码复杂度优化:

找环实现很烦很磨叽对不对?这时候我们可以引入一种新的解决方法,类似于最小树形图找环的实现(上文的实现方法我其实AC前并不知道)。

目前BZOJ排名第一你怕不怕?常数再好好写写还能更快你怕不怕?就问你怕不怕?

这里就需要运用到本题的一些性质。

首先我们可以把数据输入时的边方向当成正方向,其反方向,好吧,“当成”反方向。这样每个点都有且仅有一条出边。然后我们从任意一个点开始遍历,如果没有环,那是不是根本停不下来?!而数据又是有限的,所以最后就一定会连到已有的链上的某点,而很好想,这个点向前遍历,最后会回到这个点上,即它是一个环,而之前的点的出边方向都是在向环推进。这时候我们再把其它点挨个遍历,首先同一个联通图是一定不会有俩环的,所以要想改变“根本停不下来”的现状,新点遍历到最后,就一定会指向某个已经遍历过的点,从而连上了环,或者方向是向环的。这样我们可以把数据输入的方向存为next[i],然后把长度存为len[i]。而反向边则有邻接表存储(讨厌指针的我写的是链式前向星)。

这样我们只需要遍历任意点,通过next标记沿途所有点,然后遍历到一个已标记点,即可以高速找到基环树的环了,然后我们对环上每个点进行“-1”标记,禁止接下来的dfs找到该点(反边存了环上的边),从而可以再接下来的dfs中顺利给环上每个点赋值(找枝杈,递归求最长长度)。

优化到此结束,接下来的单调队列与一般做法无异了。

注意:

本题dfs递归出解时由于层数过深可能爆系统栈,所以需要写一份非递归版自写栈的dfs,我会在后面代码附上注释掉的递归版dfs以利于理解思想。(不要想着开大系统栈!那还不如手写非递归dfs呢!)

/**************************************************************Problem: 1791User: 18357Language: C++Result: AcceptedTime:11052 msMemory:120100 kb
****************************************************************/#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <iostream>
#define N 1050000
using namespace std;
struct Syndra
{int u,v,len,next;
}e[N];
struct Fiona
{int edge,flag1,flag2;long long temp,max1,max2;
}s[N];
int head[N],cnt,n;
int visit[N],next[N],len[N];
int i,j,k;
long long sa[N],pre[N],ans;
void add(int u,int v,int len)
{cnt++;e[cnt].u=u;e[cnt].v=v;e[cnt].len=len;e[cnt].next=head[u];head[u]=cnt;
}
int que[N<<1];
long long sum[N<<1],ret;
long long dp(int num)
{int top,tail;int u,b,star;int et;for(et=1;et<(num<<1);et++){sum[et]=sum[et-1]+pre[(et-1)>=num?(et-1-num):(et-1)];}top=tail=0;que[top]=0;for(et=1;et<(num<<1);et++){while(et-que[top]>=num)top++;u=que[top];ret=max(ret,sa[et>=num?et-num:et]+sa[u>=num?u-num:u]+sum[et]-sum[u]);b=que[tail];que[++tail]=et;for(star=tail;star>top;b=que[star-1]){if(sum[et]-sum[b]+sa[b>=num?b-num:b]<sa[et>=num?et-num:et]){que[star]=b;que[--star]=et;}else break;}tail=star;}/*que[tail++]=0;for(et=1;et<(num<<1);++et){while(top<tail&&et-que[top]>=num)++top;u=que[top];ret=max(ret,sa[et>=num?et-num:et]+sa[u>=num?u-num:u]+sum[et]-sum[u]);while(top<tail&&sa[et>=num?et-num:et]>=sa[que[tail-1]>=num?que[tail-1]-num:que[tail-1]]+sum[et]-sum[que[tail-1]])--tail;que[tail++]=et;}*/return ret;
}
void build()
{cnt=1;memset(head,0,sizeof(head));memset(visit,0,sizeof(visit));scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d%d",&next[i],&len[i]);add(next[i],i,len[i]);}
}
stack<int>sk;
int fa[N];
void dfs(int x)
{if(s[x].edge==0){sk.pop();if(s[x].flag2)ret=max(ret,s[x].max1+s[x].max2);if(visit[x]==-1)return ;x = sk.top();{int v,tt=s[x].edge;v=e[tt].v;visit[v]=i;s[x].temp=s[v].max1+e[tt].len;if(s[x].max1<s[x].temp){if(s[x].flag1)s[x].max2=s[x].max1,s[x].flag2=1;else s[x].flag1=1;s[x].max1=s[x].temp;}else if(s[x].max2<s[x].temp)s[x].max2=s[x].temp,s[x].flag2=1;s[x].edge=e[tt].next;}return ;}int v,tt=s[x].edge;v=e[tt].v;if(visit[v]==-1){s[x].edge=e[tt].next;return ;}fa[v]=x;s[v].edge=head[v];sk.push(v);
}
long long handle(int x)
{s[x].edge=head[x];sk.push(x);while(!sk.empty()){dfs(sk.top());}return s[x].max1;
}/*handle(long long)+dfs(void)=dfs(long long)*/
/*long long dfs(int x)
{int et,v,flag1,flag2;long long max1,max2;for(max1=max2=0,flag1=flag2=0,et=head[x];et;et=e[et].next){v=e[et].v;if(visit[v]==-1)continue;temp=dfs(v)+e[et].len;visit[v]=i;if(max1<temp){if(flag1)max2=max1,flag2=1;max1=temp;flag1=1;}else if(max2<temp)max2=temp,flag2=1;}if(flag2)ret=max(ret,max1+max2);return max1;
}*/
int main()
{
//  freopen("isl.in","r",stdin);
//  freopen("isl.out","w",stdout);int u,v;build();for(i=1;i<=n;i++){if(!visit[i]){for(u=i;!visit[u];u=next[u]){visit[u]=i;}if(visit[u]==i){ret=0;cnt=0;visit[u]=-1;for(v=next[u];v!=u;v=next[v]){visit[v]=-1;}v=u;do{pre[cnt]=len[v];sa[cnt++]=handle(v);v=next[v];}while(v!=u);ans+=dp(cnt);}}}cout<<ans;return 0;
}

下面是读入优化后的代码,这年头,你不读入优化,光输入就得比这代码慢啊~

/**************************************************************Problem: 1791User: 18357Language: C++Result: AcceptedTime:4556 msMemory:120132 kb
****************************************************************/#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <cctype>
#include <iostream>
#define N 1050000
using namespace std;
inline int getc() {static const int L = 1<<15;static char buf[L],*S=buf,*T=buf;if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}return *S++;
}
inline int getint() {int c;while(!isdigit(c = getc()));int tmp = c-'0';while(isdigit(c=getc()))tmp=(tmp<<1)+(tmp<<3)+c-'0';return tmp;
}
struct Syndra
{int u,v,len,next;
}e[N];
struct Fiona
{int edge,flag1,flag2;long long temp,max1,max2;
}s[N];
int head[N],cnt,n;
int visit[N],next[N],len[N];
int i,j,k;
long long sa[N],pre[N],ans;
void add(int u,int v,int len)
{cnt++;e[cnt].u=u;e[cnt].v=v;e[cnt].len=len;e[cnt].next=head[u];head[u]=cnt;
}
int que[N<<1];
long long sum[N<<1],ret;
long long dp(int num)
{int top,tail;int u,b,star;int et;for(et=1;et<(num<<1);et++){sum[et]=sum[et-1]+pre[(et-1)>=num?(et-1-num):(et-1)];}top=tail=0;/*que[top]=0;for(et=1;et<(num<<1);et++){while(et-que[top]>=num)top++;u=que[top];ret=max(ret,sa[et>=num?et-num:et]+sa[u>=num?u-num:u]+sum[et]-sum[u]);b=que[tail];que[++tail]=et;for(star=tail;star>top;b=que[star-1]){if(sum[et]-sum[b]+sa[b]<sa[et]){que[star]=b;que[--star]=et;}else break;}tail=star;}*/que[tail++]=0;for(et=1;et<(num<<1);++et){while(top<tail&&et-que[top]>=num)++top;u=que[top];ret=max(ret,sa[et>=num?et-num:et]+sa[u>=num?u-num:u]+sum[et]-sum[u]);while(top<tail&&sa[et>=num?et-num:et]>=sa[que[tail-1]>=num?que[tail-1]-num:que[tail-1]]+sum[et]-sum[que[tail-1]])--tail;que[tail++]=et;}return ret;
}
void build()
{cnt=1;memset(head,0,sizeof(head));memset(visit,0,sizeof(visit));n=getint();for(i=1;i<=n;i++){next[i]=getint();len[i]=getint();add(next[i],i,len[i]);}
}
stack<int>sk;
int fa[N];
void dfs(int x)
{if(s[x].edge==0){sk.pop();if(s[x].flag2)ret=max(ret,s[x].max1+s[x].max2);if(visit[x]==-1)return ;x = sk.top();{int v,tt=s[x].edge;v=e[tt].v;visit[v]=i;s[x].temp=s[v].max1+e[tt].len;if(s[x].max1<s[x].temp){if(s[x].flag1)s[x].max2=s[x].max1,s[x].flag2=1;else s[x].flag1=1;s[x].max1=s[x].temp;}else if(s[x].max2<s[x].temp)s[x].max2=s[x].temp,s[x].flag2=1;s[x].edge=e[tt].next;}return ;}int v,tt=s[x].edge;v=e[tt].v;if(visit[v]==-1){s[x].edge=e[tt].next;return ;}fa[v]=x;s[v].edge=head[v];sk.push(v);
}
long long handle(int x)
{s[x].edge=head[x];sk.push(x);while(!sk.empty()){dfs(sk.top());}return s[x].max1;
}/*handle(long long)+dfs(void)=dfs(long long)*/
/*long long dfs(int x)
{int et,v,flag1,flag2;long long max1,max2;for(max1=max2=0,flag1=flag2=0,et=head[x];et;et=e[et].next){v=e[et].v;if(visit[v]==-1)continue;temp=dfs(v)+e[et].len;visit[v]=i;if(max1<temp){if(flag1)max2=max1,flag2=1;max1=temp;flag1=1;}else if(max2<temp)max2=temp,flag2=1;}if(flag2)ret=max(ret,max1+max2);return max1;
}*/
int main()
{int u,v;build();for(i=1;i<=n;i++){if(!visit[i]){for(u=i;!visit[u];u=next[u]){visit[u]=i;}if(visit[u]==i){ret=0;cnt=0;visit[u]=-1;for(v=next[u];v!=u;v=next[v]){visit[v]=-1;}v=u;do{pre[cnt]=len[v];sa[cnt++]=handle(v);v=next[v];}while(v!=u);ans+=dp(cnt);}}}cout<<ans;return 0;
}

【BZOJ1791】【IOI2008】【基环树】island(status速度第一)相关推荐

  1. 【BZOJ1791】【IOI2008】【基环树】island(status第一速度)

      1791: [Ioi2008]Island 岛屿  Time Limit: 20 Sec  Memory Limit: 162 MB Submit: 908  Solved: 159 [Su ...

  2. bzoj1791,P4381-[IOI2008]Island【基环树,树形dp,单调队列dp,树的直径】

    正题 评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P4381 题目大意 有n个岛,n条无向边(保证每个岛都有边连到).走过 ...

  3. 数据结构之基环树——骑士,Island,旅行加强版,Number of Simple Paths,Traffic Network in Numazu,Card Game

    文章目录 [ZJOI2008]骑士 [IOI2008] Island [NOIP2018 提高组] 旅行 加强版 CF1454E Number of Simple Paths Traffic Netw ...

  4. 基环树DP(bzoj 1040: [ZJOI2008]骑士)

    树:n个点n-1条边的连通图 基环树:n个点n条边的连通图,也就是一个环套着多棵树 基环树DP:找到环上任意相邻两点,断掉这两点之间的边,就形成了一棵树 之后对这两点分别进行一次树形DP即可 例题: ...

  5. 【基环树DP】[NOI2012]迷失游乐园

    题目描述 Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多 ...

  6. 基环树一些有趣的事情

    基环树,就是有一个环的树.有向基环树又分内向和外向基环树,当然也有无向的. 最近遇到的基环树真不少.有些题目赤裸裸的就告诉你,"给出一棵基环树(环套树)",但是有的题会有一些标志. ...

  7. 【NOI2012】迷失游乐园【概率期望】【换根dp】【基环树】

    传送门 题意:给一棵nnn个点的带边权树或基环树,随机选一个点作为起点,每次随机走到一个相邻未走过的位置,直到无路可走.求期望路径长度. n≤105n \leq 10^5n≤105,为基环树时环的大小 ...

  8. AtCoder Beginner Contest 266(C- G)「判凸包」「dp」「期望」「基环树」「组合数」

    abc好好好. C - Convex Quadrilateral (atcoder.jp) 思路: 判凸包,向量叉积×=|a|*|b|*sin.叉积<0即角>180°. (可以勉勉强强算我 ...

  9. 【暖*墟】#动态规划# 基环树DP的学习与练习

    因为弃置了 四边形不等式优化 ,所以DP的任务还剩下 基环树DP / 插头DP / 动态DP 当然,树形DP / 状压DP / 数位DP / 斜率优化DP 也还是要练习的...... 一 . 基环树的 ...

最新文章

  1. 绝地求生12月18日服务器信息,绝地求生12月18号几点更新维护完 2019绝地求生12月18日更新维护开服时间...
  2. Spring Boot——不同环境调用不同的配置文件解决方案
  3. 网速提高学习周——系统篇
  4. HDU4462-稻草人
  5. JPA 2.1如何成为新的EJB 2.0
  6. Redis 缓存实战——缓存、数据库一致性问题分析与解决方案
  7. linux php7 替换,linux-shell-命令替换和变量替换
  8. 试图将驱动程序添加到存储区_云存储——终于等到你,还好没放弃
  9. pageoffice提示网络连接意外错误
  10. davlik虚拟机内存管理之一——内存分配
  11. 学习笔记:利用GeoServer结合uDig发布WMS详细过程
  12. bcd 初始化库系统卷失败_开源IP地址管理系统,告别传统IP统计方式(安装部署篇)
  13. 机器学习 Machine Learning
  14. 高性能服务器电源接口,服务器电源规范解析
  15. QT QProcess 使用及实时输出回显
  16. java中的控制器_SpringMVC基础(一)_控制器
  17. TSINGSEE青犀视频平台人脸识别比对控制比对时间间隔的代码设计
  18. 转载-Android 前沿UI
  19. SQLServer之添加聚集索引
  20. 【Jsp】第六课 Jsp简介和初步使用

热门文章

  1. docker 下载mysql 8.0_docker安装mysql8.0
  2. win7 u盘 正在计算机,U盘在Win7电脑上读不出来怎么办?两种解决方法全教给你!...
  3. 浮华编思论-走在初创公司的那些事(1)
  4. matlab 梯度 什么意思,浅谈Matlab中的梯度(gradient)
  5. 为什么气象站和 AI 都测不准天气?
  6. Mesos/Omega/Borg(K8S)/Firemament对比
  7. PX4 ROS学习资源汇总
  8. poi和easyExcel基于Java操作Excel学习笔记
  9. 北漂生活-租房那些事
  10. Axure 交互学习1 基础页面跳转、鼠标悬停样式