P2700&&ybtoj【图论】1章6题【逐个击破】

逐个击破

题目

P2700


解析

首先注意:ybtoj题目有m条边,而洛谷保证只有n-1条,故输入n,k变为n,m,k(m<=5*105)
转化题意为给定一个图,图上有k个特殊点,求特殊点互不连通最小代价删边方案
首先维护有特殊点的集合代表为特殊点,作用见下文
显然可以将最小代价删边方案变成最大代价加边方案,则有贪心为按边权从大到小进行排序,然后每条边并查集判断两端是否都含特殊点,若是,则跳过,否则若有一端含特殊点,将另一端的父节点设为该端,否则随意(这里打个启发式合并也是可以的,甚至打挂也可以,比如我……)
时间复杂度:O(mlogm+mlogn)(排序边和并查集)
注意范围,要开longlong

code:(洛谷)

#include<algorithm>
#include<cstdio>
#define int long long
using namespace std;
inline bool idigit(char x){
    
    return (x<'0'|x>'9')?0:1;}
inline int read()
{
    
    
	int num=0,f=1;
	char c=0;
	while(!idigit(c=getchar())){
    
    if(c=='-')f=-1;}
	while(idigit(c))num=(num<<1)+(num<<3)+(c&15),c=getchar();
	return num*f;
}
inline void write(int x)
{
    
    
	int F[20];
	int tmp=x>0?x:-x;
	if(x<0)putchar('-');
	int cnt=0;
	while(tmp>0){
    
    F[cnt++]=tmp%10+'0';tmp/=10;}
	while(cnt>0)putchar(F[--cnt]);
	if(x==0)putchar('0');
}
int n,k,t,f[100010],tot[100010],ans=0;
struct ed{
    
    int x,y,z;}a[100010];
bool u[100010];
inline bool cmp(ed x,ed y){
    
    return x.z>y.z;}
inline int find(int x){
    
    return (f[x]==x)?(x):(f[x]=find(f[x]));}
inline bool merge(int x,int y)
{
    
    
	x=find(x),y=find(y);
	if(u[x]&&u[y])return 1;
	if(x==y)return 0;
	if(u[x])f[y]=x;
	else if(u[y])f[x]=y;
	else if(tot[x]<tot[y])f[x]=y;//这个启发式合并太假了
	else
	{
    
    
		f[y]=x;
		if(tot[x]==tot[y])++tot[x];
	}
	return 0;
}
signed main()
{
    
    
	n=read(),k=read();
	for(int i=1;i<=n;++i)f[i]=i,tot[i]=1;
	while(k--)t=read(),u[t]=1;
	for(int i=1;i<n;++i)a[i].x=read(),a[i].y=read(),a[i].z=read();
	sort(a+1,a+n,cmp);
	for(int i=1;i<n;++i)if(merge(a[i].x,a[i].y))ans+=a[i].z;
	write(ans);
	return 0;
}

code:(ybtoj)

#include<algorithm>
#include<cstdio>
#define int long long
using namespace std;
inline bool idigit(char x){
    
    return (x<'0'|x>'9')?0:1;}
inline int read()
{
    
    
	int num=0,f=1;
	char c=0;
	while(!idigit(c=getchar())){
    
    if(c=='-')f=-1;}
	while(idigit(c))num=(num<<1)+(num<<3)+(c&15),c=getchar();
	return num*f;
}
inline void write(int x)
{
    
    
	int F[20];
	int tmp=x>0?x:-x;
	if(x<0)putchar('-');
	int cnt=0;
	while(tmp>0){
    
    F[cnt++]=tmp%10+'0';tmp/=10;}
	while(cnt>0)putchar(F[--cnt]);
	if(x==0)putchar('0');
}
int n,m,k,t,f[100010],ans=0;
struct ed{
    
    int x,y,z;}a[500010];
bool u[100010];
inline bool cmp(ed x,ed y){
    
    return x.z>y.z;}
inline int find(int x){
    
    return (f[x]==x)?(x):(f[x]=find(f[x]));}
inline bool merge(int x,int y)
{
    
    
	x=find(x),y=find(y);
	if(u[x]&&u[y]&&x!=y)return 1;
	if(u[x])f[y]=x;
	else f[x]=y;//终于放弃启发式合并
	return 0;
}
signed main()
{
    
    
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;++i)f[i]=i;
	while(k--)t=read(),u[t+1]=1;
	for(int i=1;i<=m;++i)a[i].x=read()+1,a[i].y=read()+1,a[i].z=read();
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=m;++i)if(merge(a[i].x,a[i].y))ans+=a[i].z;
	write(ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zhanglili1597895/article/details/115253086
今日推荐