【wqs 二分】AcWing 347. 野餐规划

好像没见到用 wqs 二分写的 emm,使用 wqs 二分的复杂度为 \(O(n {\rm log} n)\)吊打这题

不熟悉 wqs 二分的可以先看:

https://www.acwing.com/file_system/file/content/whole/index/content/5873556/

分析

记号约定:\(s\)\(1\),也就是 \(\texttt{Park}\)\(k\)\(s\) 点的最大度数。

对下凸壳二分斜率找切线。

切线即便是会与多个点相切也不要紧,我们找到最靠左的切点以及横坐标为 \(k\) 的点,求出它们的 \(y\) 坐标并取 \(\min\) 即可。

而因为可能出现最小生成树在横坐标为 \(k\) 的点的左边的情况(这意味着我们可以直接求最小生成树),那么就对此求一遍最小生成树就是答案了。

更多细节见代码:

// Problem: 野餐规划
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/349/
// Memory Limit: 10 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;

#define int long long

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=1e5+5, M=1e6+50;

struct Edge{
	int u, v, w;
	
	bool operator < (const Edge &o)const{
		return w<o.w;
	}
}e[M], es[M], buf[M];

int tot, tots, totb;

int f[N];

int find(int x){
	return x==f[x]? x: f[x]=find(f[x]);
}

int n, m, s, k;
int res, cnt;

int cal(int x){
	rep(i,1,tots) es[i].w-=x;
	res=0, totb=0;
	rep(i,1,n) f[i]=i;
	
	for(int i=1, j=1; i<=tots || j<=tot; ){
		if(i>tots || j<=tot && e[j].w<=es[i].w) buf[++totb]=e[j++];
		else buf[++totb]=es[i++];
	}
	
	cnt=0;
	rep(i,1,totb){
		int u=buf[i].u, v=buf[i].v, w=buf[i].w;
		int pu=find(u), pv=find(v);
		if(pu!=pv){
			f[pu]=pv;
			if(u==s || v==s) cnt++;
			res+=w;
		}
	}
	rep(i,1,tots) es[i].w+=x;
	return cnt;
}

map<string, int> mp;

int get(string x){
	if(!mp.count(x)) return mp[x]=++n;
	return mp[x];
}

signed main(){
	cin>>m;
	s=1;
	get("Park");
	
	rep(i,1,m){
		string su, sv; cin>>su>>sv;
		int u, v, w;
		u=get(su), v=get(sv);
		read(w);
		if(u==s || v==s) es[++tots]={u, v, w};
		else e[++tot]={u, v, w};
	}
	rep(i,1,n) f[i]=i;
	
	cin>>k;
	sort(es+1, es+1+tots), sort(e+1, e+1+tot);
	
	cal(0);
	if(cnt<=k) return cout<<"Total miles driven: "<<res<<endl, 0;
	
	int l=-30010, r=-l;
	while(l<r){
		int mid=l+r+1>>1;
		if(cal(mid)<=k) l=mid;
		else r=mid-1;
	}
	cal(l);
	int t=res;
	int fir=l*cnt, sec=1e9;
	if(cal(30010)>=k) sec=l*k;
	cout<<"Total miles driven: "<<t+min(fir, sec)<<endl;
	
	return 0;
}
posted @ 2022-06-21 21:17  HinanawiTenshi  阅读(62)  评论(0编辑  收藏  举报