2020.7.17 T3水管(jz暑假训练day3)

Description

在这里插入图片描述

Input在这里插入图片描述

Output在这里插入图片描述

Sample Input

1
5 7
1 2 2
1 4 1
2 4 2
4 3 2
2 3 1
4 5 1
1 5 2

Sample Output

5
No

Data Constraint

在这里插入图片描述

赛时

最小生成树打一遍,之后贪心的认为选一条没有选的边中最小的边,然后再打一遍最小生成树,竟然还有62分!之后把long long开了有82分!!!

正解

很多方法,我的方法大概是,先最小生成树,之后对于每个没选的边u,v,w,我们查找u到v的路径上最大的边权,那么这个边权一定不可能大于w,那么考虑能不能=w,如果可以的话说明我们可以用这个没选的边来替换这个最大的边权。这时就不是唯一的。然后树上任意两点路径最大值就用倍增,树剖来搞都行,因为练练手,所以后面我就搞了树剖(调了好几个晚上)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200007
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
using namespace std;

int n,m,cnt,f[N],head[N<<1],maxtr[N<<2],w[N],dep[N],size[N],hson[N],top[N],id[N],num;
ll ans;
bool b[N];

struct node{
	int u,v,w;
}e[N<<1];

bool cmp(node a,node b){return a.w<b.w;}

struct tree{
	int to,w,nxt;
}tree[N<<1];

void init(){
	cnt=0;num=0;ans=0;
	mem(f);mem(head);mem(maxtr);;
	mem(w);mem(dep);mem(size);mem(hson);
	mem(top);mem(id);mem(b);mem(e);mem(tree);
}

int get(int x){return x==f[x]?x:f[x]=get(f[x]);}

void Kruskal(){
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=n;i++) f[i]=i;
	int num=0;
	for(int i=1;i<=m;i++){
		int fau=get(e[i].u),fav=get(e[i].v);
		if(fau!=fav){
			num++;
			b[i]=true;//加入了最小生成树
			ans+=e[i].w;
			f[fau]=fav;
		}
		if(num==n-1) break;
	}
	printf("%lld\n",ans);
}

void add(int u,int v,int w){
	tree[++cnt].to=v;
	tree[cnt].w=w;
	tree[cnt].nxt=head[u];
	head[u]=cnt;
}

void build(int np,int l,int r){
	if(l+1==r){
		maxtr[np]=w[r];
		return;
	}
	int mid=l+r>>1;
	build(np<<1,l,mid);//由于找边权,所以树开的有区别
	build(np<<1|1,mid,r);
	maxtr[np]=max(maxtr[np<<1],maxtr[np<<1|1]);
}

int query(int np,int l,int r,int x,int y){
	if(x==y) return 0;
	if(x<=l&&y>=r)
		return maxtr[np];
	int mid=l+r>>1;
	int answer=0;
	if(x<mid) answer=max(answer,query(np<<1,l,mid,x,y));
	if(y>mid) answer=max(answer,query(np<<1|1,mid,r,x,y));
	return answer;
}

int pd(int x,int y){
	int answer=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])
			swap(x,y);
		answer=max(answer,query(1,1,num,id[top[x]],id[x]));
		answer=max(answer,w[id[top[x]]]);//我们将这条重链跳完后就要跳到这条重链顶端的父亲,那么这时后重链顶端和它父亲之间的权还没有比,所以这里要比一下。
		x=f[top[x]];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	answer=max(answer,query(1,1,num,id[x],id[y]));
	return answer;
}

void dfs_son(int fa,int x){
	f[x]=fa;
	dep[x]=dep[fa]+1;
	size[x]=1;
	hson[x]=0;
	for(int i=head[x];i;i=tree[i].nxt){
		int v=tree[i].to;
		if(v==fa) continue;
		dfs_son(x,v);
		size[x]+=size[v];
		if(size[hson[x]]<size[v])
			hson[x]=v;
	}
}

void dfs_chain(int tp,int x){
	top[x]=tp;
	id[x]=++num;
	if(hson[x]) dfs_chain(tp,hson[x]);
	for(int i=head[x];i;i=tree[i].nxt){
		int v=tree[i].to;
		if(v==f[x]||v==hson[x]) continue;
		dfs_chain(v,v);
	}
}

void Tree_chain_partition(){
	dfs_son(0,1);//第一遍dfs找子树大小,父亲,重儿子
	dfs_chain(1,1);//第二遍找重链,重链顶点,新的编号
	for(int i=1;i<=m;i++)//赋边权值
		if(b[i]){
			if(dep[e[i].u]<dep[e[i].v])
				w[id[e[i].v]]=e[i].w;
			else w[id[e[i].u]]=e[i].w;
		}
	build(1,1,num);//建线段树
	bool bz=false;
	for(int i=1;i<=m;i++)
		if(!b[i]){//没有加入生成树的树
			int k=pd(e[i].u,e[i].v);//u到v的路径上的最大边权
			if(k==e[i].w){
				bz=true;
				break;
			}
		}
	if(bz) printf("No\n");
	else printf("Yes\n");
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		init();//初始化
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
			scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
		Kruskal();//一遍最小生成树
		memset(f,0,sizeof(f));//数组清除一下
		for(int i=1;i<=m;i++)
			if(b[i])
				add(e[i].u,e[i].v,e[i].w),add(e[i].v,e[i].u,e[i].w);//重新加边
		Tree_chain_partition();//树剖
	}
}

感觉有些地方不够精炼啊,时间也比较慢,还是要再提升。

猜你喜欢

转载自blog.csdn.net/jay_zai/article/details/107452261
T3