【题解】Luogu P4381 [IOI2008]Island
原题传送门
题意:求基环树森林的直径(所有基环树直径之和)
首先,我们要对环上所有点的子树求出它们的直径和最大深度。然后,我们只用考虑在环上至少经过一条边的路径。那么,这种路径在环上一定有起始点和终点。(假设路径是从起始点开始,按顺时针方向走达到终点)
不妨枚举这段路径在环上的终点。由于规定了这个点和方向,我们就可以拆环了。然后是一个经典的技巧,把环上元素复制一遍,就可以枚举全部拆环方案。设环上有l个结点。那么,我们枚举终点,就相当于在长度为2l的数组上不断滑动一个长度为l的区间
剩下的问题与基环树已经没什么关系了。设环上边权的前缀和为sum,环上结点的子树的最大深度为dep
那么,在环上起始点为i,终点为j的路径能得到的长度就是\(sum_j−sum_i+dep_j+dep_i\)。既然枚举了j,我们在滑动区间时就只用维护\(dep_i−sum_i\)的最大值就可以了。这个可以用单调队列实现
时间复杂度O(n)
#include <bits/stdc++.h>
#define N 1000005
#define ll long long
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[25];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline ll Max(register ll a,register ll b)
{
	return a>b?a:b;
}
struct edge{
	int to,next,w;
}e[N<<1];
int head[N],cnt=0,du[N];
inline void add(register int u,register int v,register int w)
{
	e[++cnt]=(edge){v,head[u],w};
	head[u]=cnt,++du[v];
}
int n,t,vis[N],v[N],qu[N<<1];
ll d[N],f[N],ans,a[N<<1],b[N<<1];
inline void bfs(register int u,register int ti)
{
	vis[u]=ti;
	queue<int> q;
	q.push(u);
	while(!q.empty())
	{
		int v=q.front();
		q.pop();
		for(register int i=head[v];i;i=e[i].next)
			if(!vis[e[i].to])
			{
				q.push(e[i].to);
				vis[e[i].to]=ti;
			}
	}
}
inline void topsort()
{
	queue<int> q;
	for(register int i=1;i<=n;++i)
		if(du[i]==1)
			q.push(i);
	while(!q.empty())
	{
		int v=q.front();
		q.pop();
		for(register int i=head[v];i;i=e[i].next)
			if(du[e[i].to]>1)
			{
				d[vis[v]]=Max(d[vis[v]],f[v]+f[e[i].to]+e[i].w);
				f[e[i].to]=Max(f[e[i].to],f[v]+e[i].w);
				if((--du[e[i].to])==1)
					q.push(e[i].to);
			}
	}
}
inline void dp(register int ti,register int x)
{
	int m=0,i,l=0,r,y=x;
	do{
		a[++m]=f[y];
		du[y]=1;
		for(i=head[y];i;i=e[i].next)
			if(du[e[i].to]>1)
			{
				b[m+1]=b[m]+e[i].w;
				y=e[i].to;
				break;
			}
	}while(i);
	if(m==2)
	{
		for(i=head[y];i;i=e[i].next)
			if(e[i].to==x)
				l=Max(l,e[i].w);
		d[ti]=Max(d[ti],f[x]+f[y]+l);
		return;
	}
	for(i=head[y];i;i=e[i].next)
		if(e[i].to==x)
		{
			b[m+1]=b[m]+e[i].w;
			break;
		}
	for(register int i=1;i<m;++i)
		a[m+i]=a[i],b[m+i]=b[m+1]+b[i];
	qu[l=r=1]=1;
	for(i=2;i<m<<1;++i)
	{
		while(l<=r&&i-qu[l]>=m)
			++l;
		d[ti]=Max(d[ti],a[i]+a[qu[l]]+b[i]-b[qu[l]]);
		while(l<=r&&a[qu[r]]-b[qu[r]]<=a[i]-b[i])
			--r;
		qu[++r]=i;
	}
}
int main()
{
	n=read();
	for(register int i=1;i<=n;++i)
	{
		int v=read(),w=read();
		add(i,v,w),add(v,i,w);
	}
	for(register int i=1;i<=n;++i)
		if(!vis[i])
			bfs(i,++t);
	topsort();
	for(register int i=1;i<=n;++i)
		if(du[i]>1&&!v[vis[i]])
		{
			v[vis[i]]=1;
			dp(vis[i],i);
			ans+=d[vis[i]];
		}
	write(ans);
	return 0;
}
												
											【题解】Luogu P4381 [IOI2008]Island的更多相关文章
- P4381 [IOI2008]Island(基环树+单调队列优化dp)
		
P4381 [IOI2008]Island 题意:求图中所有基环树的直径和 我们对每棵基环树分别计算答案. 首先我们先bfs找环(dfs易爆栈) 蓝后我们处理直径 直径不在环上,就在环上某点的子树上 ...
 - 【Luogu】P4381 [IOI2008]Island
		
一.题目 Description 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时, ...
 - P4381 [IOI2008]Island
		
传送门 显然题目给的图构成一个基环树森林 对于每个基环树单独考虑,显然每个都走直径是最优的 考虑如何求出基环树的直径 把直径分为两种情况考虑,首先可以找出环 因为直径可能不在环边上,所以对每个环上节点 ...
 - luogu 4381 [IOI2008]Island 单调队列 + 基环树直径 + tarjan
		
Description 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时,每一对这样 ...
 - [题解] LuoguP4381 [IOI2008]Island
		
LuoguP4381 [IOI2008]Island Description 一句话题意:给一个基环树森林,求每棵基环树的直径长度的和(基环树的直径定义与树类似,即基环树上一条最长的简单路径),节点总 ...
 - BZOJ1791: [Ioi2008]Island 岛屿
		
BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园. 从每一个岛i出发,只建造一座桥. 桥的长度以Li表示. 公园内总共有N座桥. 尽管每 ...
 - [bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)
		
[bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...
 - bzoj1791: [Ioi2008]Island 岛屿 单调队列优化dp
		
1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1826 Solved: 405[Submit][S ...
 - IOI2008 island
		
题目链接:[IOI2008]Island 题目大意:求基环树直径(由于题目的意思其实是类似于每个点只有一个出度,所以在每个联通块中点数和边数应该是相同的,这就是一棵基环树,所以题目给出的图就是一个基环 ...
 
随机推荐
- Express全系列教程之(三):get传参
			
一.关于get请求 一般在网站开发中,get都用作数据获取和查询,类似于数据库中的查询操作,当服务器解析前台资源后即传输相应内容:而查询字符串是在URL上进行的,形如: http://localhos ...
 - 安装Nginx到linux服务器(Ubuntu)详解
			
先去下载一个nginx放到服务器. 然后解压(可参考前面安装tomcat)编译(./configure --prefix=/usr/local/nginx/server/ && mak ...
 - 在Vuex更新,组件内的视图更新问题
			
由于js的限制,vue无法进行监听数组; 当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如: vm.items.len ...
 - linux pwd命令 显示当前所在路径
			
pwd 显示当前所在路径 [root@MongoDB ~]# pwd /root
 - 【菜鸟学Python】案例一:汇率换算
			
汇率换算V1.0 案例描述: 设计一个汇率换算器程序,其功能是将外币换算成人民币,或者相反 案例分析: 分析问题:分析问题的计算部分: 确定问题:将问题划分为输入.处理及输出部分: 设计算法:计算部分 ...
 - CDIE2019中国数字化创新展暨首席信息官峰会上海站来袭~
			
China Digital Innovation Expo & CIO Summit 2019是由Dot Connector(上海华昂商务咨询有限公司)主办的第五届聚焦中国技术领袖,探索创新, ...
 - centos7.5 安装gaussian09和 gaussianview4
			
一.安装gaussian09 1. 解压安装包 $ mkdir Gaussian$ cd Gaussian$ tar xvf g09_linux.tar 2. 设置环境变量 #gaussian09 e ...
 - C# Json解析Json = "{\"EX_RETURN\":[{\"MATNR\":\"test\"}] }";
			
string jtext = "{\"jiangsu\":[{\"wuxi\":\"无锡\"},{\"suzhou\&q ...
 - Qt QLabel  显示gif动图
			
#include <QMovie> QMovie * move = new QMovie(":/gif/牵着我的手去浪迹天涯.gif"); ui->label_g ...
 - js某一元素在数组中的索引
			
第一种:数组遍历 function search(arr,dst){ var i = arr.length; while(i-=1){ if (arr[i] == dst){ return i; } ...