#3629 island(基环树)

24 篇文章 0 订阅
23 篇文章 0 订阅
描述

你将要游览一个有N个岛屿的公园。从每一个岛i出发,只建造一座桥。桥的长度以Li表示。公园内总共有N座桥。尽管每座桥由一个 岛连到另一个岛,但每座桥均可以双向行走。同时,每一对这样的岛屿,都有一艘专用的往来两岛之间的渡船。 相对于乘船而言, 你更喜欢步行。你希望所经过的桥的总长度尽可能的长,但受到以下的限制。

• 可以自行挑选一个岛开始游览。

• 任何一个岛都不能游览一次以上。

• 无论任何时间你都可以由你现在所在的岛S去另一个你从未到过的岛D。

由S到D可以有以下方法: 步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离;

或者 o 渡船:你可以选择这种方法,仅当没有任何桥和/或以前使用过的渡船的组合可以由S走到D(当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。

注意,你不必游览所有的岛,也可能无法走完所有的桥。

任务 编写一个程序,给定N座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的最大长度。

限制 2 <= N <= 1,000,000 公园内的岛屿数目。 1<= Li <= 100,000,000 桥i的长度。

输入

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

输出

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

样例输入
7
3 8
7 2
4 2
1 4
1 9
3 4
2 3
样例输出
24

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define N 2000100
#define M 4000010
#define ll long long
#define in rad()
#define inf 0x3f3f3f3f
inline int rad(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	if(c=='-')f=-1,c=getchar();
	while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
	return x*f;
}
struct Edge {
	int v,w,nxt;
} e[M];
int n,m,cnt=1;//第i与第i^1条边互为反向边由于搜索时for(int i=fir[u];i;i=e[].nxt)当i遍历至0时就退出,则边的序号不能从0开始,要满足i与i^1端点相同,则边的编号要从2开始
int first[N]= {};
void add(int u,int v,int w) {
	e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=first[u];first[u]=cnt;
}
int vis[N],mark[N],prt[N],fa[N],L[N],prtw[N],flag[M];//falg[i]表示第i条边是否被访问过 
ll mi=inf,mx,ret;
ll col[N],sum[N],tot,d[N];
int c_dfs(int x,int fa){
	vis[x]=1;
	for(int i=first[x];i;i=e[i].nxt){
		int v=e[i].v;//printf("v=%d\n",v);
		if(flag[i]&&flag[i^1])continue;
		flag[i]=flag[i^1]=1;//i为奇数则判断前一个数,i为偶数则判断后一个数 
		//if(v==fa) continue;//若有重边,即两个点组成的环,则搜索不到 
		if(vis[v]){
			col[++tot]=v;
			int tmp=e[i].w;//记录与上一个点的距离 
			mark[v]=1;//标记在环上,后记录为是否访问过 
			for(int u=x;u!=v;u=prt[u]){//记搜返回,求环上的点 
				col[++tot]=u;sum[tot]=tmp;tmp=prtw[u];//sum记录对应col记录的点与其下一个点的距离 
				mark[u]=1;
			}
			sum[1]=tmp;//视v点为终点,记录其与起点的较短的路径长度 
			return 1;//有环 
		}
		prt[v]=x;prtw[v]=e[i].w;
		if(c_dfs(v,x)) return 1;
	}
	return 0;
}
void getloop(int x){
	int u;
	for(u=fa[x];!vis[u];u=fa[u])vis[u]=1;
	int tmp=L[u];col[++tot]=u;mark[u]=1;
	for(u=fa[u];!mark[u];u=fa[u]){
		col[++tot]=u;
		sum[tot]=tmp;tmp=L[u];mark[u]=1;
	}
	sum[1]=tmp;
}
void dp(int x){
	mark[x]=1;
	for(int i=first[x];i;i=e[i].nxt){
		int v=e[i].v;
		if(mark[v])continue;//避免搜索到环上和父亲 
		dp(v);
		ret=max(ret,d[x]+d[v]+e[i].w);//d[i]记录叶子结点到i的最长距离 
		d[x]=max(d[x],d[v]+e[i].w);
	}
}
ll solve(int x){
	tot=0;
	mi=inf,mx=-inf,ret=0;
	c_dfs(x,0);
	for(int i=1;i<=tot;i++){
		//cerr<<col[i]<<" -"<<sum[i]<<" ";
		sum[i]+=sum[i-1];//求前缀和 
	}
	//cerr<<":"<<tot<<endl;
	for(int i=1;i<=tot;i++)dp(col[i]); //cerr<<ret<<endl;
	for(int i=1;i<=tot;i++){//枚举环上所有点 
		ret=max(ret,max(sum[i]+d[col[i]] - mi, sum[tot] -sum[i]+d[col[i]]+mx));
		mi=min(mi,sum[i]-d[col[i]]);
		mx=max(mx,sum[i]+d[col[i]]);
	}
	//cerr<<"ret="<<ret<<endl;
	return ret;
}
int main(){
	int size=100<<20;//40M
	__asm__ ("movl %0, %%esp\n"::"r"((char*)malloc(size)+size));//调试用这个
	//__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个
	n=in;
	for(int i=1;i<=n;i++){
		fa[i]=in;L[i]=in;
		add(fa[i],i,L[i]);add(i,fa[i],L[i]);
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
	if(!mark[i])ans+=solve(i);//枚举所有连通块 
	cout<<ans;
	exit(0);
}

残码(都不知道咋改了):
问题就是找环时,若不记录边已访问,是否有其他方法,本想甩一个判是否是父亲走人,但好像遇到重边时过不了,欢迎各位斧正!

#include<bits/stdc++.h>
using namespace std;
#define N 2000100
#define M 4000010
#define ll long long
#define in rad()
#define inf 0x3f3f3f3f
inline int rad(){
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	if(c=='-')f=-1,c=getchar();
	while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
	return x*f;
}
struct Edge {
	int v,w,nxt;
} e[M];
int n,m,cnt=1;//第i与第i^1条边互为反向边由于搜索时for(int i=fir[u];i;i=e[].nxt)当i遍历至0时就退出,则边的序号不能从0开始,要满足i与i^1端点相同,则边的编号要从2开始
int first[N]= {};
void add(int u,int v,int w) {
	e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=first[u];first[u]=cnt;
}
int vis[N],mark[N],prt[N],fa[N],L[N],prtw[N],flag[M];//falg[i]表示第i条边是否被访问过 
ll mi=inf,mx,ret;int lcz[N];
ll col[N],sum[N],tot,d[N];
int c_dfs(int x,int fa){
	vis[x]=1;memset(lcz,0,sizeof lcz);
	for(int i=first[x];i;i=e[i].nxt){
		int v=e[i].v;//printf("v=%d\n",v);
//		if(flag[i]&&flag[i^1])continue;
//		flag[i]=flag[i^1]=1;//i为奇数则判断前一个数,i为偶数则判断后一个数 
		if(v==fa&&!lcz[v]){//若有重边,即两个点组成的环,则搜索不到 
			
			for(int j=e[i].nxt;j;j=e[j].nxt){
				if(e[j].v==fa) lcz[v]++;
			}
			if(!lcz[v]) continue;
		}
		if(vis[v]){
			col[++tot]=v;
			int tmp=e[i].w;//记录与上一个点的距离 
			mark[v]=1;//标记在环上,后记录为是否访问过 
			for(int u=x;u!=v;u=prt[u]){//记搜返回,求环上的点 
				col[++tot]=u;sum[tot]=tmp;tmp=prtw[u];//sum记录对应col记录的点与其下一个点的距离 
				mark[u]=1;
			}
			sum[1]=tmp;//视v点为终点,记录其与起点的较短的路径长度 
			return 1;//有环 
		}
		prt[v]=x;prtw[v]=e[i].w;
		if(c_dfs(v,x)) return 1;
	}
	return 0;
}
void getloop(int x){
	int u;
	for(u=fa[x];!vis[u];u=fa[u])vis[u]=1;
	int tmp=L[u];col[++tot]=u;mark[u]=1;
	for(u=fa[u];!mark[u];u=fa[u]){
		col[++tot]=u;
		sum[tot]=tmp;tmp=L[u];mark[u]=1;
	}
	sum[1]=tmp;
}
void dp(int x){
	mark[x]=1;
	for(int i=first[x];i;i=e[i].nxt){
		int v=e[i].v;
		if(mark[v])continue;//避免搜索到环上和父亲 
		dp(v);
		ret=max(ret,d[x]+d[v]+e[i].w);//d[i]此时记录所有树中的最大直径 
		d[x]=max(d[x],d[v]+e[i].w);
	}
}
ll solve(int x){
	tot=0;
	mi=inf,mx=-inf,ret=0;
	c_dfs(x,0);
	for(int i=1;i<=tot;i++){
		//cerr<<col[i]<<" -"<<sum[i]<<" ";
		sum[i]+=sum[i-1];//求前缀和 
	}
	//cerr<<":"<<tot<<endl;
	for(int i=1;i<=tot;i++)dp(col[i]); //cerr<<ret<<endl;
	for(int i=1;i<=tot;i++){//枚举环上所有点 
		ret=max(ret,max(sum[i]+d[col[i]] - mi, sum[tot] -sum[i]+d[col[i]]+mx));
		mi=min(mi,sum[i]-d[col[i]]);
		mx=max(mx,sum[i]+d[col[i]]);
	}
	//cerr<<"ret="<<ret<<endl;
	return ret;
}
int main(){
	int size=100<<20;//40M
	__asm__ ("movl %0, %%esp\n"::"r"((char*)malloc(size)+size));//调试用这个
	//__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个
	n=in;
	for(int i=1;i<=n;i++){
		fa[i]=in;L[i]=in;
		add(fa[i],i,L[i]);add(i,fa[i],L[i]);
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
	if(!mark[i])ans+=solve(i);//枚举所有连通块 
	cout<<ans;
	exit(0);
}

AC代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=1e6+5;
int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=(s<<3)+(s<<1)+ch-'0';
		ch=getchar();
	}
	return s*f;
}
struct fjy{
	int nxt,w,v;
}e[N<<1];
int fir[N],mark[N],pr[N],prw[N],vis[N],flag[N<<1];
int n,cnt=1,v,w,tmp;
ll mi=inf,mx,ret,ans;
ll col[N],sum[N],tot,d[N];
int cdfs(int u,int fa){
	vis[u]=1;
	for(int i=fir[u];i;i=e[i].nxt){
		if(flag[i]&&flag[i^1]) continue;
		flag[i]=flag[i^1]=1;
		if(vis[e[i].v]){
			tmp=e[i].w;
			col[++tot]=e[i].v;mark[e[i].v]=1; 
			for(int x=u;x!=e[i].v;x=pr[x]){
				col[++tot]=x;sum[tot]=tmp;tmp=prw[x];
				mark[x]=1;
			}
			sum[1]=tmp;
			return 1;
		}
		pr[e[i].v]=u,prw[e[i].v]=e[i].w;
		if(cdfs(e[i].v,u)) return 1;
	}
	return 0;
}
int max(int x,int y){
	return x>y?x:y;
}
void dp(int u){
	mark[u]=1;
	for(int i=fir[u];i;i=e[i].nxt){
		if(mark[e[i].v]) continue;
		dp(e[i].v);
		ret=max(ret,d[u]+d[e[i].v]+e[i].w);
		d[u]=max(d[u],d[e[i].v]+e[i].w);
	}
}
ll solve(int u){
	tot=0;
	mi=inf,mx=-inf,ret=0;
	cdfs(u,0);
	for(int i=1;i<=tot;i++)
		sum[i]+=sum[i-1];
	for(int i=1;i<=tot;i++) dp(col[i]);
	for(int i=1;i<=tot;i++){
		ret=max(ret,max(sum[i]+d[col[i]]-mi,sum[tot]-sum[i]+d[col[i]]+mx));
		mi=min(mi,sum[i]-d[col[i]]);
		mx=max(mx,sum[i]+d[col[i]]);
	} 
	return ret;
}
void add(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nxt=fir[u];
	fir[u]=cnt;
}
int main(){
	int size=100<<20;
	//__asm__ ("movl %0, %%esp\n"::"r"((char*)malloc(size)+size));//调试用这个
	__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个
	n=read();
	for(int i=1;i<=n;i++){
		v=read(),w=read();
		add(i,v,w),add(v,i,w);
	}
	for(int i=1;i<=n;i++) if(!mark[i]) ans+=solve(i);
	printf("%lld",ans);
	exit(0);//不能用return 0结束
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值