bzoj2654:tree
传送门
wqs二分果题
就是切线对应多个点有点烦,别的很好想
当切线对应多个点时,就取横坐标最小的决策点,最后取横坐标最大的决策点求答案。
代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define rg register
void read(int &x){
char ch;bool ok=0;
for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
if(ok)x=-x;
}
const int maxn=1e5+10;
int f[maxn],n,m,k,ans,num;
struct oo{int x,y,z,id;}a[maxn],g[maxn];
bool cmp(oo a,oo b){return a.z==b.z?a.id>b.id:a.z<b.z;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
bool check(int now){
for(rg int i=1;i<=n;i++)f[i]=i;
int tot=0,sum=0;num=0;
for(rg int i=1;i<=m;i++)g[++tot]=a[i],g[tot].z=!a[i].id?g[tot].z-now:g[tot].z;
sort(g+1,g+m+1,cmp);
for(rg int i=1;i<=m;i++)
if(find(g[i].x)!=find(g[i].y)){
sum+=(g[i].id==0),num+=g[i].z;
f[find(g[i].x)]=find(g[i].y);
}
return sum<=k;
}
int main(){
read(n),read(m),read(k);
for(rg int i=1,x,y,z,op;i<=m;i++){
read(x),read(y),read(z),read(op);x++,y++;
a[i]=(oo){x,y,z,op};
}
int l=-105,r=105;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))l=mid+1,ans=num+mid*k;
else r=mid-1;
}
printf("%d\n",ans);
}