luogu P5633 最小度限制生成树
题面传送门
这种强制选\(k\)的一般就是wqs二分。
我们考虑二分一个数,然后将所有和\(s\)相连的边加上这个数。
然后跑完wqs二分看看是不是\(k\)条边,如果大于则增大左边界,反之缩小右边界。
这样时间复杂度是\(O(mlogmlogv)\)的过不去。
可以发现时间复杂度瓶颈在排序上。
我们每次只对固定的边减去一个值,所以我们可以分别排序然后归并。时间复杂度就变成\(O(mlogm+vlogm)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 500000
#define M 1000
#define mod 31011
#define eps (1e-7)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,un,wn,s,k,Ah,Bh,Ch,l,r,mid,tot,x,y,z,fa[N+5];ll ans;
struct ques{int x,y,w;}A[N+5],B[N+5],C[N+5];I bool cmp(ques x,ques y){return x.w<y.w;}
I void swap(int &x,int &y){x^=y^=x^=y;}
I int Getfa(int x){return x==fa[x]?x:fa[x]=Getfa(fa[x]);}
I int check(int mid){
re int i,j;for(i=1;i<=Bh;i++)B[i].w+=mid;Ah=tot=ans=0;for(i=1;i<=n;i++) fa[i]=i;
for(i=j=1;i<=Bh||j<=Ch;)A[++Ah]=(j>Ch||(i<=Bh&&B[i].w<=C[j].w)?B[i++]:C[j++]);
for(i=1;i<=Ah;i++){//printf("%d %d\n",i,Ah);
un=Getfa(A[i].x);wn=Getfa(A[i].y);if(un==wn) continue;
ans+=A[i].w;fa[un]=wn;tot+=(A[i].x==s);
}
for(i=1;i<=Bh;i++) B[i].w-=mid;return tot>=k;
}
I ll solve(int s,int k){
l=-1e9;r=1e9;while(l+1<r) mid=l+r>>1,(check(mid)?l:r)=mid;check(l);if(tot^k){printf("Impossible\n");exit(0);};return ans-1ll*l*k;
}
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d%d%d%d",&n,&m,&s,&k);for(i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),(y==s)&&(swap(x,y),0),(x==s?B[++Bh]:C[++Ch])=(ques){x,y,z};if(Bh<k){printf("Impossible\n");return 0;}
sort(B+1,B+Bh+1,cmp);sort(C+1,C+Ch+1,cmp);printf("%lld\n",solve(s,k));
}