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; }

版权声明:本文博主原创文章。博客,未经同意不得转载。

转载于:https://www.cnblogs.com/lcchuguo/p/4850271.html

【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. 基环树一些有趣的事情

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

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

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

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

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

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

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

  8. CodeForces - 1454E Number of Simple Paths(基环树+思维)

    题目链接:点击查看 题目大意:给出一棵 n 个点的基环树,现在需要求所有长度大于等于 1 的路径个数 题目分析:对于所有的路径 ( x , y ) 可以分成下列两种情况来考虑: 路径不会经过环上的边: ...

  9. CodeForces - 1252L Road Construction(基环树+有源汇有上下界的最大流)

    题目链接:点击查看 题目大意:给出 n 个节点,再给出 n 个出边,保证所有的边能将 n 个点连通,每条出边可以用 m[ i ] 种材料选择其一建造,然后有 k 个工人,每个工人只可以使用一种材料建造 ...

最新文章

  1. 端口映射问题:Bad Request This combination of host and port requires TLS.
  2. V-rep对UR3机械臂仿真路径规划
  3. 个人开发者 android内购,【开发者账号】关于内购,协议税务的一些坑
  4. 3个提升Python运行速度的方法,很实用
  5. 81-spark异常总结1
  6. 九度OnlineJudge之1001:A+B for Matrices
  7. python-编程之美
  8. 类818tu.c微信小说分销系统设计之定时模板消息源码
  9. 永中office linux卸载,永中集成Office在Ubuntu下的安装和卸载方法
  10. VSCode安装LeetCode插件
  11. C# Color颜色RGB对照表
  12. python用openpyxl模块操作Excel学习笔记
  13. 关于Windows 10驱动无法使用,无法验证设备所需的驱动程序的数字签名问题
  14. C++/openCV修改视频分辨率
  15. js中对象属性、面向对象、面向过程、类、继承、以及原型原型链
  16. Vivado工程配置petalinux实现linux下网卡驱动
  17. Ubuntu16.04系统安装搜狗输入法详细教程
  18. iOS项目开发实战——iOS网络编程获取网页Html源代码
  19. Flutter项目错误解决:/.pub-cache/hosted/pub.dartlang.org/flutter_advanced_networkimage-0.5.0
  20. 利用Python输出九九乘法表

热门文章

  1. Bacon分解做交错DID数据、代码以及参考文献
  2. 百度云盘试用时间本地CE修改
  3. 淘宝封杀selenium的ua算法分析
  4. 微信支付V3版开发中遇到的一个问题及原因:缺少prepay_id
  5. Flutter 动态化新知识
  6. Google - Colab实验室 - 柯基犬猫咪
  7. 2013年5月中国数码相机市场分析报告
  8. 外汇基础学习1:结售汇
  9. Android 状态栏工具——一行代码实现状态栏字体变黑
  10. KBQA知识问答概述