[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的前驱即可。

扫描二维码关注公众号,回复: 9256909 查看本文章

可以使用平衡树维护,时间复杂度\(O({\sum}n\log 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('\n');
    }
};

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;
}

猜你喜欢

转载自www.cnblogs.com/xh092113/p/12330056.html