uoj198【CTSC2016】时空旅行

传送门:http://uoj.ac/problem/198

【题解】

首先y、z是没有用的。。

然后式子就是w = (x0-xi)^2+ci的最小值,化出来可以变成一个直线的形式。

然后我们可以用线段树维护dfs序上的每个点。

每个点维护经过这个点的所有直线(标记永久化),也就是维护上凸壳。

然后我们把询问按照x排序,每次决策点只会后移。所以复杂度就有保证啦!

真**难写

还有一个十分有趣的事实啊

我用一个号交完ac在另一个号再交就RE了啊。。。

不管了反正过了

# include <vector>
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, q, s[M], ps[M];
ll c0, ans[M];
vector<int> b[M];
struct planet {
    int x; ll c;
    planet() {}
    planet(int x, ll c) : x(x), c(c) {}
    friend bool operator < (planet a, planet b) {
        return a.x<b.x;
    }
}p[M];

struct line {
    ll k, b;
    line() {}
    line(ll k, ll b) : k(k), b(b) {}
    inline ll set(int x) {
        return k*(ll)x+b;
    }
};

struct quest {
    int s, x0, id;
    quest() {}
    quest(int s, int x0, int id) : s(s), x0(x0), id(id) {}
    friend bool operator < (quest a, quest b) {
        return a.x0<b.x0;
    }
}qu[M];

int head[M], nxt[M], to[M], tot, in[M], out[M], DFN;
inline void add(int u, int v) {
    ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
}

inline void dfs(int x) {
    in[x] = ++DFN;
    for (int i=head[x]; i; i=nxt[i]) dfs(to[i]);
    out[x] = DFN;
}

inline bool cmp(int x, int y) {
    return p[x].x<p[y].x;
}

inline bool cmp_b(int x, int y) {
    return in[x] < in[y];
}

namespace SMT {
    const int M = 2e6 + 10;
    vector<line> w[M];
    int lst[M];

    inline bool canout(line a, line b, line c) {
        if(b.k == c.k) return b.b >= c.b;
        return 1.0*(b.b-a.b)*(a.k-c.k) > 1.0*(c.b-a.b) * (a.k-b.k);
    }

    inline void change(int x, int l, int r, int L, int R, line add) {
        if(L <= l && r <= R) {
            if(w[x].size()) {
                while(w[x].size() > 1 && canout(w[x][w[x].size()-2], w[x][w[x].size()-1], add)) w[x].pop_back();
                w[x].push_back(add);
            } else w[x].push_back(add);
            return ;
        }
        int mid = l+r>>1;
        if(L <= mid) change(x<<1, l, mid, L, R, add);
        if(R > mid) change(x<<1|1, mid+1, r, L, R, add);
    }

    inline ll query(int x, int l, int r, int pos, int x0) {
        while(lst[x]+1 < w[x].size() && w[x][lst[x]].set(x0) > w[x][lst[x]+1].set(x0)) ++lst[x];
        ll cur = (w[x].size() ? w[x][lst[x]].set(x0) : 1e18);
        if(l == r) return cur;
        int mid = l+r>>1;
        if(pos <= mid) return min(cur, query(x<<1, l, mid, pos, x0));
        else return min(cur, query(x<<1|1, mid+1, r, pos, x0));
    }

    inline void change(int L, int R, line add) {
        if(L>R) return;
        change(1, 1, DFN, L, R, add);
    }
    inline ll query(int pos, int x0) {
        return query(1, 1, DFN, pos, x0);
    }
}

int main() {
    scanf("%d%d%lld", &n, &q, &c0);
    for (int i=1, op, fr, id, x, c; i<n; ++i) {
        ps[i] = i;
        scanf("%d%d%d", &op, &fr, &id);
        if(op == 0) {
            scanf("%d%*d%*d%lld", &x, &c);
            s[id] = i;
            p[id] = planet(x, c);
        } else b[id].push_back(i);
        add(fr, i);
    }
    dfs(0);
    sort(ps+1, ps+n, cmp);
    for (int i=1, t; i<n; ++i) {
        if(s[ps[i]]) {
            int id = ps[i], se = s[id];
            line L = line(-2ll*p[id].x, 1ll*p[id].x*p[id].x+p[id].c);
            if(b[id].size()) {
                sort(b[id].begin(), b[id].end(), cmp_b);
                t = b[id].size();
                SMT::change(in[se], in[b[id][0]]-1, L);
                SMT::change(out[b[id][t-1]]+1, out[se], L);
                for (int j=1; j<t; ++j)
                    SMT::change(out[b[id][j-1]]+1, in[b[id][j]]-1, L);
            } else SMT::change(in[se], out[se], L);
        }
    }
    for (int i=1; i<=q; ++i) {
        scanf("%d%d", &qu[i].s, &qu[i].x0);
        qu[i].id=i;
    }
    sort(qu+1, qu+q+1);
    for (int i=1; i<=q; ++i)
        ans[qu[i].id] = 1ll * qu[i].x0 * qu[i].x0 + min(c0, SMT::query(in[qu[i].s], qu[i].x0));
    for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
    return 0;
}

时间: 2024-11-05 05:35:10

uoj198【CTSC2016】时空旅行的相关文章

[CTSC2016]时空旅行(线段树+凸包)

应该是比较套路的,但是要A掉仍然不容易. 下面理一下思路,思路清楚了也就不难写出来了. 0.显然y,z坐标是搞笑的,忽略即可. 1.如果x不变,那么直接set即可解决. 2.考虑一个空间和询问x0,通过化式子发现实际上就是:把每个星球看成一个一次函数,其实是在询问这个空间内的所有一次函数在x0处的最小值. 3.这个显然是一个凸包,所以我们需要对每个空间维护一个凸包,由空间整体呈树状,可以想到用DFS序+线段树维护区间. 4.预处理出每个星球的存在范围,在线段树上永久化标记.查询时依次递归求最小值

[CTSC2016]时空旅行

description 题面 solution 线段树分治+斜率优化毒瘤题 题目可以简化为: 你要维护一个包含元素\((x,c)\)的集合 修改操作为从以前的一个版本更新,修改内容为添加或删除一个元素 查询操作给出\(x_0\),查询某个版本中的\(max\{(x-x_0)^2+c\}\) 可以知道版本之间的时间关系形成一颗树 如果在一个版本删除了某个元素,那么在这个版本的子树中都不会再有这个版本 由于子树的\(dfn\)是连续的,因此操作可以简化为在序列上进行,总共有\(O(m)\)个区间 最

luogu P5416 [CTSC2016]时空旅行

luogu uoj 注意到用这个集合产生方式可以构建出一个树型结构,并且每个加入/删除元素都是对应的一个子树的范围,对应到dfs序上就是每次对一个区间内的集合加入/删除元素,所以可以线段树分治,把每种元素的出现区间整出来 把答案柿子\((x-x_0)^2+c\)拆开,得到\(x^2-2x*x_0+{x_0}^2+c\),然后每个元素就相当于斜率为\(-2x\),截距为\(x^2+c\)的直线,所以线段树每个节点维护凸壳,要用的时候直接查对应横坐标的值.如果直接做是两个\(log\)的,但是因为要

Luogu P5416 [CTSC2016]时空旅行(线段树分治)

题目 简化题意:你需要维护\(n\)个集合,集合元素为二元组\((x,v)\).集合\(i\)的产生方式是以某个原有集合\(p_i\)为样本,扩展或删除一个元素后得到新集合.有\(q\)次询问,每次给出\(y\)并指定一个集合\(i\),要求从集合\(i\)中找出一个元素,最小化\((x?y)^2+v\). 先拆式子\((x-y)^2+v=x^2-2xy+y^2+v\),令其等于\(k\)即\(x^2+y^2-2xy+v=k\). 移项得\(2yx+k=y^2+x^2+v\),可以看作是\((x

@loj - [email&#160;protected] 「CTSC2016」时空旅行

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 2045 年,人类的技术突飞猛进,已经找到了进行时空旅行的方法.小 R 得到了一台时空旅行仪,他想用它调查不同时空中人类的发展状况. 根据平行时空理论,宇宙中存在着很多独立的时空,每个时空在下一个时间点还会分化出若干个不同的时空.宇宙是一个三维空间,人类使用空间直角坐标系来描述空间中的

时空旅行的可能性(无聊研究社)

经济不好的时候,人一般都没有什么爱好,所以这段时间只能以看相对论啊,宇宙起源之类的东西拿来消遣,说实话这类消遣是最廉价的,成本不会高于宅男们欣赏苍老师们的表演的成本.仔细的阅读了一些这些大师们的著作后,我发现,时空旅行理论上可以,但实际上很难,因为: 1)如果靠速度去旅行,宇宙天体之间的距离动不动就是多少光年,也就是说光都要跑好多年,要实现早上出发晚上到啥恒星去吃晚饭的梦想,非常的困难,一是很难达到光的速度,就是达到了,也动不动就要好几年:所以要达到星际朝发夕至的梦想,靠速度是不行的.2)靠速度

[UOJ198]时空旅行

看懂题目就知道$y,z$是没用的,这题相当于是给一堆$(x_i,c_i)$和询问$x_q$,要求$(x_q-x_i)^2+c_i$的最大值 先把这个式子拆开:$-2x_ix_q+x_i^2+c_i+x_q^2$,那么询问就是求一堆直线$y=-2x_ix+x_i^2+c_i$在$x=x_q$处的最小值,维护坐标轴底端的上凸壳即可 再看题目中关于“时空”的限制,其实就是给一棵树,某一些点会有标记表示以这个点为根的子树内有/没有一条直线,我们用dfs序在线段树上覆盖对应区间并预处理出每个线段树节点的凸

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什

线段树分治总结

目录 类型一 例题1:八纵八横 代码: 例题2:时空旅行 首先,要求可以离线. 线段树分治有两种. 类型一 操作基于区间,单点询问. 有时,进行的一种操作可以快速完成,但是,要实现这种操作的逆操作较难. 因为,通常情况下,需要实现的逆操作都是很久以前执行的. 但是,如果只撤销上次操作,就会简单得多. 比如,维护一些连通性,或直径,线性基等问题. 这类问题加边很好做,但删边很难实现. 我们可以扫一遍操作,得到每个操作的有效区间. 然后,将每个添加操作的有效区间按在线段树上,然后遍历这颗线段树同时处