题解:P16146 [ICPC 2017 NAIPC] Blazing New Trails
lailai0916 · · 题解
题意简述
在带权无向图中选出一棵生成树。部分顶点为特殊点。要求生成树中恰有
解题思路
称连接两类顶点的边为异类边。给每条异类边的边权增加整数
随着
固定
固定某个
边权均为整数。按异类边数限制得到的最优值具有离散凸性,所以相邻限制的分界斜率也是整数。只要目标异类边数可行,必能在某个整数
原边权位于
不必在每次二分中重新排序全部边。分别按原边权排序普通边和异类边。增加
正确性证明
对于固定
若
二分始终保留可能使
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<ll,int>;
const int N=200005;
const int M=500005;
struct Edge
{
int u,v,w;
}e[2][M];
int n,cnt[2],fa[N],siz[N];
bool sp[N];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
pii mst(int x,bool op)
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
siz[i]=1;
}
int p[2]={},num=0,res=0;
ll sum=0;
while(num<n-1&&(p[0]<cnt[0]||p[1]<cnt[1]))
{
bool t;
if(p[0]==cnt[0])t=1;
else if(p[1]==cnt[1])t=0;
else
{
int a=e[0][p[0]].w,b=e[1][p[1]].w+x;
t=op?b<=a:b<a;
}
Edge a=e[t][p[t]++];
int u=find(a.u),v=find(a.v);
if(u==v)continue;
if(siz[u]>siz[v])swap(u,v);
fa[u]=v;
siz[v]+=siz[u];
num++;
res+=t;
sum+=a.w+t*x;
}
return num==n-1?pii(sum,res):pii(0,-1);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m,k,w;
cin>>n>>m>>k>>w;
for(int i=0;i<k;i++)
{
int x;
cin>>x;
sp[x]=1;
}
for(int i=0;i<m;i++)
{
int u,v,c;
cin>>u>>v>>c;
bool t=sp[u]!=sp[v];
e[t][cnt[t]++]={u,v,c};
}
for(int i=0;i<2;i++)sort(e[i],e[i]+cnt[i],[](const Edge &a,const Edge &b){return a.w<b.w;});
pii mx=mst(-100000,1),mn=mst(100000,0);
if(mx.second<0||mn.second>w||mx.second<w)
{
cout<<-1<<'\n';
return 0;
}
int l=-100000,r=100001;
while(l<r)
{
int x=(l+r)>>1;
mx=mst(x,1);
mn=mst(x,0);
if(mn.second<=w&&w<=mx.second)
{
cout<<mx.first-(ll)w*x<<'\n';
return 0;
}
if(mx.second<w)r=x;
else l=x+1;
}
return 0;
}