• [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;
    }
    
  • 相关阅读:
    poj 2754 Similarity of necklaces 2 转换成多重背包,单调队列优化/ 二进制优化
    new和delete2
    new和delete1
    new和delete4
    new和delete3
    new(placement new)
    用例图中的Actor(参与者)一定是人吗?
    二维数组的函数参数传递
    二维指针动态创建二维数组(C/C++)
    OOD的五项基本原则——SOLID
  • 原文地址:https://www.cnblogs.com/xh092113/p/12330056.html
Copyright © 2020-2023  润新知