jzoj3860-地殼運動(mst)【最小生成樹,三分】

正題

題目鏈接:https://jzoj.net/senior/#contest/show/3002/1


題目大意

nn個點mm條邊,每條邊有(u,v)(u,v)兩個權值。

qq個詢問,每次詢問一個(k1,k2)(k1,k2),將所有邊的權值變爲uk1+vk2u*k1+v*k2後求最小生成樹。


解題思路

首先uk1+vk2(u+vk2k1)k1u*k1+v*k2\Rightarrow (u+v*\frac{k2}{k1})*k1,所以決策可以線性表示出來。

我們考慮維護一個座標系(x,y)(x,y)表示uu值和爲xx的生成樹中yy值和最小是多少。

顯然xx增大時yy在減小,所以這是一個下突殼。

考慮維護這一個突殼,首先是兩個端點l:(k1=0,k2=1)l:(k1=0,k2=1)時和r:(k1=1,k2=0)r:(k1=1,k2=0)各做一次最小生成樹,此時我們考慮找到一個在線段(l,r)(l,r)的左下角最遠的點midmid

通過差積各種證明後我們發現有mid(k1=ylyr,k2=xlxr)mid(k1=|y_l-y_r|,k2=|x_l-x_r|),然後分治下去處理(l,mid)(l,mid)(r,mid)(r,mid)

midmid在線段(l,r)(l,r)上時,證明左下角已經沒有更優的點,所以可以返回了。

最後在突殼上三分答案就好了。


codecode

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int M=25100,N=40;
struct node{
	int x,y;
	double u,v;
}a[M];
struct knode{
	double k1,k2,x,y;
}k[M*4],head,tail;
int n,m,q,fa[N],tot;
double k1,k2;
int find(int x)
{return fa[x]==x?x:(fa[x]=find(fa[x]));}
bool cmp(node x,node y)
{return x.u*k1+x.v*k2<y.u*k1+y.v*k2;}
void Get_Tree(knode &k){
	k1=k.k1;k2=k.k2;
	k.x=k.y=0;
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	int z=n;
	for(int i=1;i<=m;i++){
		int fx=find(a[i].x),fy=find(a[i].y);
		if(fx!=fy){
			if(fx>fy) swap(fx,fy);
			fa[fy]=fx;z--;
			k.x+=a[i].u;k.y+=a[i].v;
		}
	}
	return;
}
void Solve(knode l,knode r){
	knode mid;
	mid.k1=fabs(r.y-l.y);
	mid.k2=fabs(r.x-l.x);
	Get_Tree(mid);
	if(l.y==mid.y||l.y==r.y||(l.x-r.x)/(l.y-r.y)==(l.x-mid.x)/(l.y-mid.y))return;
	Solve(l,mid);k[++tot]=mid;
	Solve(mid,r);
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;i++)
		scanf("%d%d%lf%lf",&a[i].x,&a[i].y,&a[i].u,&a[i].v);
	head.k1=1;tail.k2=1;
	Get_Tree(head);Get_Tree(tail);
	k[tot=1]=head;Solve(head,tail);k[++tot]=tail;
	while(q--){
		scanf("%lf%lf",&k1,&k2);
		int l=1,r=tot;
		double ans=1e18;
		while(l<r){
			if(r-l<=2)break;
			int mid1=l+(r-l+1)/3,mid2=l+(r-l+1)/3*2;
			if(k[mid1].x*k1+k[mid1].y*k2<k[mid2].x*k1+k[mid2].y*k2) r=mid2;
			else l=mid1;
		}
		for(int i=l;i<=r;i++)
			ans=min(k[i].x*k1+k[i].y*k2,ans);
		printf("%.3lf\n",ans);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章