[HDU6637]Speed Dog(闵可夫斯基和+平衡树)

题面

http://acm.hdu.edu.cn/showproblem.php?pid=6637

题解

前置知识

题目中的(a_ix_i)(b_i(1-x_i))容易让人想到定比分点公式。考虑建立一个平面直角坐标系,横轴为A轴,纵轴为B轴。那么,第i个任务可以视为一条从((0,b_i))((a_i,0))的线段,这条线段上、距离左端点为全长(x)倍的点((x{in}[0,1])),其坐标正好为((a_ix,b_i(1-x)))

所以,初始时可置((X,Y)=(0,0))。对于第i个任务,可以视作从((0,b_i))((a_i,0))的线段上,选取某一点,然后把这一点所对应的向量加入((X,Y))。求最终(max(X,Y))的最小值。

发现对于第k个询问,即加入了前k条线段后,所有可能的((X,Y))构成的点集即为前k条线段所构成的闵可夫斯基和。线段的凸包仅由两条互反的有向线段组成,所以运用归纳法,结合凸形闵和的性质,可以证明前k条线段构成的闵和是一个中心对称图形。

然后这个图形中,横纵坐标最大值最小的点就是直线(A-B=0)与此图形的第一个交点。

发现凸包的上半部分没什么用,所以只考虑下半部分。

考虑用数据结构维护,每次要做的就是插入一条线段:按极角为关键字找到插入的位置,把左边的所有线段向上平移(b_i),右边的所有线段向右平移(a_i)

  • 如图,黑色为原凸包,插入一条橙色线段,粉色+橙色为新凸包

查询交点时,由于这里的所有线段的极角都在((-{frac{pi}{2},0}))之间,所以这里所有线段的起始点的“横坐标-纵坐标”值也是有序的,按此为关键字,查找0的前驱即可。

可以使用平衡树维护,时间复杂度(O({sum}nlog n))

  • P.S.由于是在学习计算几何期间做的此题,所以用了闵和的方法;但是此题有非计算几何的做法,详见https://blog.csdn.net/ehdhg13455/article/details/98966356

代码

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>

using namespace std;

#define ll long long
#define rg register
#define In inline
#define N 250000
#define inf 0x3f3f3f3f3f3f3f3f

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

In ll write(ll x){
	if(x < 0)x = -x,putchar('-');
	if(x > 9)write(x / 10);
	putchar('0' + x % 10);
}

In ll gcd(ll a,ll b){
	return b ? gcd(b,a % b) : a;
}

struct vec{
	ll x,y;
	vec(){}
	vec(ll _x,ll _y){x = _x,y = _y;}
	In friend vec operator + (vec a,vec b){
		return vec(a.x + b.x,a.y + b.y);
	}
	In friend vec operator - (vec a,vec b){
		return vec(a.x - b.x,a.y - b.y);
	}
	In friend ll Dot(vec a,vec b){
		return a.x * b.x + a.y * b.y;
	}
	In friend ll Cross(vec a,vec b){
		return a.x * b.y - a.y * b.x;
	}
};

struct line{
	vec p,v;
	line(){}
	line(vec _p,vec _v){p = _p,v = _v;}
	In friend void printits(line a){ //输出直线a与直线x=y的交点的横坐标
		ll x = Cross(vec() - a.p,a.v),y = Cross(a.v,vec(1,1));
		ll d = gcd(x,y);
		write(x / d),putchar('/'),write(y / d),putchar('
');
	}
};

ll a[N+5],b[N+5];

struct Splay{
	ll rt,cnt;
	ll fa[N+5],c[N+5][2],id[N+5];
	vec flag[N+5],p[N+5];
	void clear(){
		rt = cnt = 0;
	}
	In void reset(int u,int f,int i){
		c[u][0] = c[u][1] = 0;
		flag[u] = vec();
		fa[u] = f;
		id[u] = i;
	}
	In void pushdown(int u){
		if(!flag[u].x && !flag[u].y)return;
		if(c[u][0])p[c[u][0]] = flag[u] + p[c[u][0]],flag[c[u][0]] = flag[c[u][0]] + flag[u];
		if(c[u][1])p[c[u][1]] = flag[u] + p[c[u][1]],flag[c[u][1]] = flag[c[u][1]] + flag[u];
		flag[u] = vec();
	}
	void rotate(int u){
		int f = fa[u],g = fa[f],k = c[f][1] == u,w = c[u][!k];
		if(g)c[g][c[g][1]==f] = u;
		fa[f] = u;
		c[f][k] = w;
		fa[u] = g;
		c[u][!k] = f;
		if(w)fa[w] = f;
	}
	void splay(int u,int goal){
		while(fa[u] != goal){
			int f = fa[u],g = fa[f];
			if(g != goal){
				if((c[f][1]==u) ^ (c[g][1]==f))rotate(u);else rotate(f);
			}
			rotate(u);
		}
		if(!goal)rt = u;
	}
	void insert(int i){
		if(!rt){
			rt = ++cnt;
			reset(cnt,0,i);
			return; 
		}
		int u = rt,v;bool k;
		while(1){
			pushdown(u);
			v = c[u][k=(Cross(vec(a[i],-b[i]),vec(a[id[u]],-b[id[u]]))<=0)];
			if(!v)break;
			u = v;
		}
		c[u][k] = ++cnt;
		reset(cnt,u,i);
		splay(cnt,0);
	}
	void pro(){
		pushdown(rt);
		int lc = c[rt][0],rc = c[rt][1];
		flag[rc] = flag[rc] + vec(a[id[rt]],0); p[rc] = p[rc] + vec(a[id[rt]],0);
		flag[lc] = flag[lc] + vec(0,b[id[rt]]); p[lc] = p[lc] + vec(0,b[id[rt]]);
		pushdown(rt);
		int u = c[rt][1]; //u:根结点rt的后缀
		while(c[u][0])pushdown(u),u = c[u][0];
		p[rt] = p[u] + vec(-a[id[rt]],b[id[rt]]);
	}
	line pred(){ //寻找并输出p.x<p.y的最后一个结点
		ll ans = 1,maxn = -inf;
		ll i = rt;
		while(i){
			pushdown(i);
			if(p[i].x < p[i].y){
				if(p[i].x - p[i].y > maxn)maxn = p[i].x - p[i].y,ans = i;
				i = c[i][1];
			}
			else i = c[i][0];
		}
		return line(p[ans],vec(a[id[ans]],-b[id[ans]]));
	}
}S;

int main(){
//	freopen("H6637.in","r",stdin);
//	freopen("H6637.out","w",stdout);
	ll T = read();
	while(T--){
		ll n = read();
		S.clear();
		a[0] = 0,b[0] = 1,a[n+1] = 1,b[n+1] = 0;
		S.insert(0);S.p[S.cnt] = vec(0,1);     //
		S.insert(n + 1);S.p[S.cnt] = vec(0,0); //第一和第二号节点,用来防止溢出 
		for(rg int i = 1;i <= n;i++){
			a[i] = read(),b[i] = read();
			S.insert(i);
			S.pro();
			printits(S.pred());		
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/xh092113/p/12330056.html