传送门
解析:
乍一看完全没思路的一道题。
搜索?显然不行。。。
然而,看出正解的我无f**k说。。。
思路:
考虑怎么调整白边的个数。在做最小生成树的时候。
我们可以通过给白边全部加上或减去一个权值来搞定。
加上一个数,可以使白边在生成树里面的条数减少。
加少一点,就会使白边在生成树里面的条数增加。
这个是很显然的。并且是对白色边的条数影响是具有单调性的。
但是问题就来了,怎么知道我们这样做是对的?
严格证明我就不放了,敲起来有点麻烦,有需要的在评论区评论并点赞,人多了我就更新严格证明。(应该没人吧,毕竟这次的感性理解还是比较清楚的 )
下面放感性理解。
把黑色边和白色边分别放在两个排好序的队列里面(woc,就不用排序那么多次了,我写代码的时候怎么没想到,应该比直接排序更快啊。。。)
然后做
k
r
u
s
k
a
l
kruskal
kruskal生成树的时候显然就是直接在两个队首取。
把白色加上或减去一个权值,就会直接影响有几个黑色会在它前面被取出,
我们可以通过这种方式调整白色出来的次数。
那么显然,这样就是对的。
代码(全部排序):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));
num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline
void outint(int a){
static char ch[13];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
struct edge{
int u,v,w;
bool col;
edge(){}
edge(int u,int v,int w,bool col):u(u),v(v),w(w),col(col){}
friend bool operator<(cs edge &a,cs edge &b){
return a.w==b.w?a.col<b.col:a.w<b.w;
}
}e[100002];
int n,m,need;
int fa[50002];
inline
int getfa(int x){
return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
inline
void merge(int i,int j){
i=getfa(i);
j=getfa(j);
fa[i]=j;
}
int weight;
inline
bool check(int mid){
for(int re i=1;i<=n;++i){
fa[i]=i;
}
for(int re i=1;i<=m;++i){
if(!e[i].col)e[i].w+=mid;
}
sort(e+1,e+m+1);
int cnt=0,tot=0;
weight=0;
for(int re i=1;i<=m;++i){
int u=getfa(e[i].u),v=getfa(e[i].v);
if(u==v)continue;
++tot;
merge(u,v);
weight+=e[i].w;
if(!e[i].col){
++cnt;
}
if(tot==n-1)break;
}
for(int re i=1;i<=m;++i){
if(!e[i].col)e[i].w-=mid;
}
return cnt>=need;
}
signed main(){
n=getint(),m=getint(),need=getint();
for(int re i=1;i<=m;++i){
int u=getint()+1,v=getint()+1,w=getint(),col=getint();
e[i]=edge(u,v,w,col);
}
int l=-100,r=100;
int ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))l=mid+1,ans=weight-need*mid;
else r=mid-1;
}
cout<<ans<<endl;
return 0;
}
代码(双队列)有问题,但是暂时不想改:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline
int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));
num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline
void outint(int a){
static char ch[13];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
struct edge{
int u,v,w;
edge(){}
edge(int u,int v,int w):u(u),v(v),w(w){}
friend bool operator<(cs edge &a,cs edge &b){
return a.w<b.w;
}
}bl[100002],wh[100002];
int b,h;
int n,m,need;
int fa[50002];
inline
int getfa(int x){
return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
inline
void merge(int i,int j){
i=getfa(i);
j=getfa(j);
fa[i]=j;
}
int weight;
inline
bool check(int mid){
for(int re i=1;i<=n;++i){
fa[i]=i;
}
int bhead=1,whead=1;
int cnt=0,tot=0;weight=0;
while(bhead<=b||whead<=h){
if((bhead<=b&&bl[bhead].w<wh[whead].w+mid)||whead>h){
edge e=bl[bhead++];
int u=getfa(e.u),v=getfa(e.v);
if(u==v)continue;
merge(u,v);
++tot;
weight+=e.w;
}
else{
edge e=wh[whead++];
int u=getfa(e.u),v=getfa(e.v);
if(u==v)continue;
merge(u,v);
++tot;
++cnt;
weight+=e.w;
}
if(tot==n-1)break;
}
return cnt>=need;
}
signed main(){
n=getint(),m=getint(),need=getint();
for(int re i=1;i<=m;++i){
int u=getint()+1,v=getint()+1,w=getint(),col=getint();
if(col)bl[++b]=edge(u,v,w);
else wh[++h]=edge(u,v,w);
}
sort(bl+1,bl+b+1);
sort(wh+1,wh+h+1);
int l=-100,r=100;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<weight<<endl;
return 0;
}