http://uoj.ac/problem/198

(先补一下以前的题解)

这道题5分暴力好写好调,链上部分分可以用可持久化线段树,每次旅行\(x\)值相同的可以用标记永久化线段树。我还听到某些神犇说可以用KD-Treebalabalabala。


很显然\(y,z\)坐标都没用。然后。。。



在一个时空中,对于固定的\(x_0\),要求最小的\(ans=c_i+(x_i-x_0)^2\),拆开可以得到\(ans-x_0^2=(c_i+x_i^2)-2x_ix_0\),这样只需要求\(min_i\{(c_i+x_i^2)-2x_ix_0\}\)。

把\(c_i+x_i^2\)看成\(y\)轴上的截距,\(-2x_i\)看成斜率,相当于一个\(y=kx+b\)的一次函数。在一个时空中有若干个一次函数,我们要求\(x=x_0\)时这些函数的最小值,很明显只要维护一个上凸壳,在上凸壳上查找就可以了。



对每个时空都维护一个上凸壳是必须的,暴力对每个星球所影响的时空都添加一条一次函数时间代价太高了。

还可以把每一部决策都抽象成一棵树,这样一个在第\(i\)个时空中殖民了一个星球,这个星球的一次函数能影响的其他时空是它在决策树中以\(i\)为根的子树。

对决策树的dfs序建线段树,以\(i\)为根的子树在dfs序中构成了一个连续的区间。在线段树上能表示这个区间的节点上都加上一次函数所带来的影响(标记永久化)。

至于放弃殖民的星球,只要记录一下星球在哪个时空中放弃了,线段树update的时候避开这些放弃的时空中的区间就可以了。

那么怎么维护线段树每个节点上的凸壳呢?

splay?复杂度\(O(n\log^2n)\)?

其实只要先对每个星球的\(x\)值从小到大排序,保证加入的一次函数的斜率单调递减,就可以用单调栈维护了,初始化复杂度\(O(n\log n)\)。



对于每个询问,可以直接从线段树的根节点到线段树的叶子节点的路径上所有的节点统计一下\(x=x_0\)时的最小值,最后和地球的值取min。

那么怎么统计一个凸壳上\(x=x_0\)时的最小值呢?

二分?复杂度\(O(n\log^2n)\)?

其实只要对询问的\(x_0\)从小到大排序,这样在凸壳上的位置也是单调递增的,每个凸壳记录上一次在哪里取得最小值,下一次的位置一定在其之后,回答询问时间复杂度\(O(n\log n)\)。



主要采用了离线排序后利用单调性的思想QAQ。

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 500003; ll c0;
struct Edge {int nxt, to;} E[N];
int n, m, cnt = 0, point[N], tmp[N];
vector <int> del[N]; void ins(int u, int v) {E[++cnt] = (Edge) {point[u], v}; point[u] = cnt;} struct node {
int x; ll c;
node(int _x = 0, ll _c = 0) : x(_x), c(_c) {}
bool operator < (const node &A) {
return x < A.x;
}
} Planet[N]; int L_dfn[N], R_dfn[N], settle_to[N]; void dfs(int x) {
L_dfn[x] = ++cnt;
for(int i = point[x]; i; i = E[i].nxt)
dfs(E[i].to);
R_dfn[x] = cnt;
} struct Querynode {
int s, x0, id;
bool operator < (const Querynode &A) const {
return x0 < A.x0;
}
} Q[N]; ll ans[N]; struct Knode {
int k; ll b;
Knode(int _k = 0, ll _b = 0) : k(_k), b(_b) {}
ll cal(int x) {return b + 1ll * k * x;}
}; namespace SegmentTree {
vector <Knode> Tu[N << 2];
int tmp[N << 2];
bool Cross(Knode a, Knode b, Knode c) {
if (b.k == c.k) return c.b <= b.b;
return 1.0 * (b.b - a.b) * (a.k - c.k) > 1.0 * (c.b - a.b) * (a.k - b.k);
}
void update(int rt, int l, int r, int L, int R, Knode Line) {
if (L <= l && r <= R) {
if (!Tu[rt].size()) Tu[rt].push_back(Line);
else {
while (Tu[rt].size() > 1 && Cross(Tu[rt][Tu[rt].size() - 2], Tu[rt].back(), Line)) Tu[rt].pop_back();
Tu[rt].push_back(Line);
}
return;
}
int mid = (l + r) >> 1;
if (L <= mid) update(rt << 1, l, mid, L, R, Line);
if (R > mid) update(rt << 1 | 1, mid + 1, r, L, R, Line);
}
ll query(int rt, int l, int r, int pos, int x) {
while (tmp[rt] + 1 < Tu[rt].size() && Tu[rt][tmp[rt]].cal(x) > Tu[rt][tmp[rt] + 1].cal(x)) ++tmp[rt];
ll ans = Tu[rt].size() ? Tu[rt][tmp[rt]].cal(x) : 100000000000000000ll;
if (l == r) return ans;
int mid = (l + r) >> 1;
if (pos <= mid) return min(ans, query(rt << 1, l, mid, pos, x));
else return min(ans, query(rt << 1 | 1, mid + 1, r, pos, x));
}
} ll sqr(int x) {return 1ll * x * x;}
bool cmp_233(int x, int y) {return Planet[x].x < Planet[y].x;}
bool cmp1(int x, int y) {return L_dfn[x] < L_dfn[y];} int main() {
scanf("%d%d%lld", &n, &m, &c0);
int op, fr, id, x, y, z; ll c;
for(int i = 1; i < n; ++i) {
tmp[i] = i;
scanf("%d%d%d", &op, &fr, &id);
if (op == 0) {
scanf("%d%d%d%lld", &x, &y, &z, &c);
settle_to[id] = i;
Planet[id] = node(x, c);
} else
del[id].push_back(i);
ins(fr, i);
} cnt = 0;
dfs(0);
Knode Line; int pl, sp; stable_sort(tmp + 1, tmp + n, cmp_233);
for(int i = 1; i < n; ++i) if (settle_to[tmp[i]]) {
pl = tmp[i];
sp = settle_to[pl];
Line = Knode(-2 * Planet[pl].x, sqr(Planet[pl].x) + Planet[pl].c);
if (!del[pl].size()) {if (L_dfn[sp] <= R_dfn[sp]) SegmentTree::update(1, 1, n, L_dfn[sp], R_dfn[sp], Line);}
else {
stable_sort(del[pl].begin(), del[pl].end(), cmp1);
if (L_dfn[sp] < L_dfn[del[pl][0]]) SegmentTree::update(1, 1, n, L_dfn[sp], L_dfn[del[pl][0]] - 1, Line);
if (R_dfn[del[pl].back()] < R_dfn[sp]) SegmentTree::update(1, 1, n, R_dfn[del[pl].back()] + 1, R_dfn[sp], Line);
for(int j = 0; j < del[pl].size() - 1; ++j)
if (R_dfn[del[pl][j]] + 1 < L_dfn[del[pl][j + 1]]) SegmentTree::update(1, 1, n, R_dfn[del[pl][j]] + 1, L_dfn[del[pl][j + 1]] - 1, Line);
}
} for(int i = 1; i <= m; ++i) {
scanf("%d%d", &Q[i].s, &Q[i].x0);
Q[i].id = i;
} sort(Q + 1, Q + m + 1);
for(int i = 1; i <= m; ++i)
ans[Q[i].id] = min(sqr(Q[i].x0) + c0, sqr(Q[i].x0) + SegmentTree::query(1, 1, n, L_dfn[Q[i].s], Q[i].x0)); for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
return 0;
}



终于AC了

【UOJ #198】【CTSC 2016】时空旅行的更多相关文章

  1. @loj - 2987@ 「CTSC2016」时空旅行

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 2045 年,人类的技术突飞猛进,已经找到了进行时空旅行的方法. ...

  2. 【UOJ #201】【CTSC 2016】单调上升路径

    http://uoj.ac/problem/201 别人都一眼秒的题对我而言怎么那么难qwq 这道题就是要构造一个n*n的邻接矩阵,满足矩阵\(A\)是一个拉丁方阵(也是数独?),\(a_{ij}=a ...

  3. 【CTSC2016】时空旅行

    链接 http://uoj.ac/problem/198 题解 首先要发现答案要我们求这个式子: \[ ans=min\bigl((x_i-x)^2+c_i\bigr) \] 显而易见的是这种时空嫁接 ...

  4. uoj198【CTSC2016】时空旅行

    传送门:http://uoj.ac/problem/198 [题解] 首先y.z是没有用的.. 然后式子就是w = (x0-xi)^2+ci的最小值,化出来可以变成一个直线的形式. 然后我们可以用线段 ...

  5. [UOJ198][CTSC2016]时空旅行

    uoj description 你要维护若干个集合,每个集合都是有一个编号比他小的集合扩展而来,扩展内容为加入一个新的元素\((x,c)\)或者删除一个已有元素.集合的扩展关系之间构成一个树形结构. ...

  6. [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 ...

  7. Luogu P5416 [CTSC2016]时空旅行

    第一次写线段树分治的题目,没想到是道这么毒的题233 首先发现题目里的\((x,y,z,c)\)就是在放屁,只有\((x,c)\)是有用的 因此我们可以把题意转化为,在某一个时间节点上,求出所有元素的 ...

  8. 【NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2】游记

    我第一次写游记,,,, 正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪 ...

  9. UOJ#54 BZOJ3434 [WC2014]时空穿梭

    题目描述 小 X 驾驶着他的飞船准备穿梭过一个 \(n\) 维空间,这个空间里每个点的坐标可以用 \(n\) 个实数表示,即 \((x_1,x_2,\dots,x_n)\). 为了穿过这个空间,小 X ...

随机推荐

  1. 【poj1222-又一道开关问题】高斯消元求解异或方程组

    题意:给出一个5*6的图,每个灯泡有一个初始状态,1表示亮,0表示灭.每对一个灯泡操作时,会影响周围的灯泡改变亮灭,问如何操作可以使得所有灯泡都关掉. 题解: 这题和上一题几乎完全一样..就是要输出解 ...

  2. 【CC2530入门教程-02】CC2530的通用I/O端口输入和输出控制

    第2课  CC2530的通用I/O端口输入和输出控制 小蜜蜂科教 / 广东职业技术学院  欧浩源 [通用I/O端口视频教程:https://v.qq.com/x/page/x0793aol7us.ht ...

  3. js 的function为什么可以添加属性

    (1) function person(){ this.name = 'Tom'; } (2) function person(){} person.name = 'Tom'; (3) functio ...

  4. 【shell】shell编程(六)-shell函数的应用

    linux shell 可以用户定义函数,然后在shell脚本中可以随便调用. shell中函数的定义格式如下: [ function ] funname [()] { action; [return ...

  5. C++之参数总结

    函数的形参为函数提供了已命名的局部存储空间,它是在函数的形参表中定义的,并由调用函数时传递给函数的实参初始化,而形参的 初始化与变量的初始化一样,如果形参具有非引用类型,则复制实参的值,如果形参为引用 ...

  6. 64_d2

    dtc-1.4.4-2.fc26.x86_64.rpm 20-Jun-2017 11:04 89890 dtdinst-20131210-7.fc26.noarch.rpm 11-Feb-2017 0 ...

  7. Django2.0如何配置urls文件

    刚开始学django,创建的第一个工程无法启动,后来发现是由于教程是针对较低版本的Django,我用的是Django2.0和Python3.6,两个都是发文为止的最新版本,urls文件设置方法和旧版本 ...

  8. VPS性能测试方法小结(8)

    1.为了能够得到更为准确和详细的有关VPS主机性能测试数据,我们应该多角度.全方位地运行多种VPS性能测试工具来进行检测,同时也要记得排除因本地网络环境而造成的数据结果的错误. 2.VPS主机性能跑分 ...

  9. .net连接sql server的几种连接字符串的写法

    .net连接sql server的几种连接字符串的写法 1, 混合验证模式登录 server=电脑名 或 电脑IP;database=数据库名;uid=数据库登录名;password=数据库登录密码 ...

  10. 动画基础--基于Core Animation(1)

    1.简介 上一篇文章[New learn]动画-基于UIView了解到了一些直接由UIView这个在UIKIT提供的类中提供的一些动画方法. 使用UIView的动画特性已经能够满足我们很多的需求,它是 ...