bzoj3533: [Sdoi2014]向量集 线段树分治+凸包+三分

bzoj3533: [Sdoi2014]向量集

Description

维护一个向量集合,在线支持以下操作:
“A x y (|x|,|y| < =10^8)”:加入向量(x,y);
” Q x y l r (|x|,|y| < =10^8,1 < =L < =R < =T,其中T为已经加入的向量个数)询问第L个到第R个加入的向量与向量(x,y)的点积的最大值。
集合初始时为空。

Input

输入的第一行包含整数N和字符s,分别表示操作数和数据类别;
接下来N行,每行一个操作,格式如上所述。
请注意s≠'E'时,输入中的所有整数都经过了加密。你可以使用以下程序

得到原始输入:
inline int decode (int x long long lastans) {
return x ^ (lastans & Ox7fffffff);
}
function decode
begin
其中x为程序读入的数,lastans为之前最后一次询问的答案。在第一次询问之前,lastans=0。
注:向量(x,y)和(z,W)的点积定义为xz+yw。

Output

对每个Q操作,输出一个整数表示答案。

Sample Input

6 A
A 3 2
Q 1 5 1 1
A 15 14
A 12 9
Q 12 8 12 15
Q 21 18 19 18

Sample Output

13
17
17

解释:解密之后的输入为
6 E
A 3 2
Q 1 5 1 1
A 2 3
A 1 4
Q 1 5 1 2
Q 4 3 2 3

HINT

1 < =N < =4×10^5
新加数据一组..2015.315

分析

考虑暴力
点积起来其实就是类似给一条定斜率直线去切点。
然后维护一下凸包就可以了。
由于加了时间,所以线段树分治。
把点插入线段树,只在区间插满的时候搞凸包。
因为点有正负,所以上下凸壳都要维护。。。。
新加的数据有long long
之前用vector的时候时间爆了。
所以改成邻接表+内存池。
巨烦。

代码

#include
#include
typedef long long LL; 
const int N = 10485763, T = 1048576; const LL inf = 9223372036854775807;
char S[7] = "E", o[7]; LL La;
LL Rl(LL x) {return S[0] == 'E' ? x : x ^ (La & 0x7fffffff);}
LL ri() {
    char c = getchar(); LL x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return Rl(x * f);
}
int n, pr[T << 1], nx[N], tp, C; LL Nw;
struct P {LL x, y; P(LL _x = 0, LL _y = 0) : x(_x), y(_y) {}}Cu, to[N];
void add(int u, P v) {to[++C] = v; nx[C] = pr[u]; pr[u] = C;}
P operator - (P a, P b) {return P(a.x - b.x, a.y - b.y);}
LL operator ^ (P a, P b) {return a.x * b.y - a.y * b.x;}
LL operator * (P a, P b) {return a.x * b.x + a.y * b.y;}
P M[N], c[T]; int sz;
struct A {
    P *s; int tp;
    void rsz(int n) {tp = 0; s = M + sz + 1; sz += n + 1;}
    P& operator [] (int x) {return s[x];}
}up[T << 1], dw[T << 1];
bool cmp(P a, P b) {return a.x == b.x ? a.y < b.y : a.x < b.x;}
void Up(LL &a, LL b) {a < b ? a = b : 0;}
LL Ask(int u, P p) {
    A &c = p.y > 0 ? up[u] : dw[u];
    int L = 1, R = c.tp, m1, m2;
    for(;R - L >= 3; )
        p * c[m1 = ((L << 1) + R) / 3] < p * c[m2 = ((R << 1) + L) / 3] ? L = m1 : R = m2;
    LL r = -inf; for(int i = L; i <= R; ++i) Up(r, p * c[i]);
    return r;
}
void Con(int u) {
    tp = 0; for(int i = pr[u]; i; i = nx[i]) c[++tp] = to[i];
    std::sort(c + 1, c + tp + 1, cmp); up[u].rsz(tp); dw[u].rsz(tp); int t = 0;
    for(int i = 1;i <= tp; up[u][++t] = c[i++])
        for(;t > 1 && ((up[u][t] - up[u][t - 1]) ^ (c[i] - up[u][t - 1])) >= 0;) --t;
    up[u].tp = t; t = 0;
    for(int i = 1;i <= tp; dw[u][++t] = c[i++])
        for(;t > 1 && ((dw[u][t] - dw[u][t - 1]) ^ (c[i] - dw[u][t - 1])) <= 0;) --t;
    dw[u].tp = t; 
}
void Add(int p, int L, int R, int x) {
    add(p, Cu); if(R == x) Con(p);
    if(L == R) return ; int m = L + R >> 1;
    x <= m ? Add(p << 1, L, m, x) : Add(p << 1 | 1, m + 1, R, x);
}
void Que(int p, int L, int R, int st, int ed) {
    if(L == st && ed == R) return void(Up(Nw, Ask(p, Cu)));
    int m = L + R >> 1;
    if(st <= m) Que(p << 1, L, m, st, std::min(ed, m));
    if(ed > m) Que(p << 1 | 1, m + 1, R, std::max(st, m + 1), ed);
}
int main() {
    n = ri(); scanf("%s", S);
    for(int i = 1, x, y, t = 0; i <= n; ++i) {
        scanf("%s", o); Cu.x = ri(); Cu.y = ri(); Nw = -inf;
        if(o[0] == 'A') Add(1, 1, n, ++t);
        else x = ri(), y = ri(), Que(1, 1, n, x, y), printf("%lld\n", La = Nw);
    }
    return 0;
}

你可能感兴趣的:(分治结构-线段树分治,数学相关-计算几何)