BZOJ2654 tree(wqs二分)

有关于wqs二分的问题,一般伴随着选指定个数的数量,且数量选取的多少和方法都会影响答案的一种问题。

这种问题一般都具有凸性,也就是斜率单增单减,这样我们就可以套上wqs二分来优化。

这基于的原理可以观看wqs本人的论文。

对于这题,我们发现选取白边的数量对答案是由影响的,也可以证明他确实具有凸性。

我们二分权值,对于每个白边都加上这个权值,之后做一遍最小生成树。如果使用的白边数量不够,说明我们需要往左边二分,也就是我们希望白边的个数增多。

反之就往右边二分,但是很多题目这样二分是可能出不了结果的,这是因为,我们附加的权值即使变化1,白边数量不一定变化1,因为我们可能在相同权值的白边和黑边上选择了黑边。

因此我们考虑排序,将白边先选,这样我们肯定可以刚好变化成要求数量。很多题目都要像这样附加一个条件。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pll;
const int N=2e6+10;
const int M=2e6+10;
const int inf=0x3f3f3f3f;
int p[N],n,m,k;
ll ans;
struct node{
    int a,b,c,id;
}e[N],s[N];
bool cmp(node a,node b){
    if(a.c==b.c)
        return a.id<b.id;
    else
        return a.c<b.c;
}
int find(int x){
    if(p[x]!=x){
        p[x]=find(p[x]);
    }
    return p[x];
}
int check(int x){
    int i;
    int tot=0;
    int cnt=0;
    ans=0;
    for(i=1;i<=m;i++){
        s[i]=e[i];
        if(e[i].id==0)
            s[i].c+=x;
    }
    for(i=1;i<=n;i++)
        p[i]=i;
    sort(s+1,s+1+m,cmp);
    for(i=1;i<=m;i++){
        int pa=find(s[i].a);
        int pb=find(s[i].b);
        if(pa!=pb){
            tot++;
            if(!s[i].id)
                cnt++;
            p[pa]=pb;
            ans+=s[i].c;
        }
        if(tot==n-1)
            break;
    }
    return cnt>=k;
}
int main(){
    ios::sync_with_stdio(false);
    int i;
    cin>>n>>m>>k;
    for(i=1;i<=m;i++){
        cin>>e[i].a>>e[i].b>>e[i].c>>e[i].id;
        e[i].a++;
        e[i].b++;
    }
    int l=-100,r=100;
    int us=0;
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)){
            l=mid;
        }
        else
            r=mid-1;
    }
    check(l);
    cout<<ans-l*k<<endl;
}
View Code

 

posted @ 2020-09-09 17:12  朝暮不思  阅读(121)  评论(0编辑  收藏  举报