逐个击破
题目
解析
首先注意: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;
}