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

应该是比较套路的,但是要A掉仍然不容易。

下面理一下思路,思路清楚了也就不难写出来了。

0.显然y,z坐标是搞笑的,忽略即可。

1.如果x不变,那么直接set即可解决。

2.考虑一个空间和询问x0,通过化式子发现实际上就是:把每个星球看成一个一次函数,其实是在询问这个空间内的所有一次函数在x0处的最小值。

3.这个显然是一个凸包,所以我们需要对每个空间维护一个凸包,由空间整体呈树状,可以想到用DFS序+线段树维护区间。

4.预处理出每个星球的存在范围,在线段树上永久化标记。查询时依次递归求最小值。

5.关于线段树上如何存凸包,可以先扫一遍预留出空间,也可以直接像http://www.cnblogs.com/HocRiser/p/8549456.html一样用vector存,后者可能会慢一点,开了读入外挂就过了。

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4 #define ls (x<<1)
 5 #define rs (ls|1)
 6 #define lson ls,L,mid
 7 #define rson rs,mid+1,R
 8 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 9 typedef long long ll;
10 using namespace std;
11
12 const int N=500010;
13 const ll inf=1000000000000000000ll;
14 int n,m,cnt,x,y,c,tim,op,fr,d,id[N],L[N],R[N],X[N],K[N];
15 int h[N],nxt[N],pos[N],to[N],st[N<<2],ed[N<<2],q[N];
16 ll a[N],b[N],ans[N];
17 vector<int>T[N<<2],V[N];
18
19 ll rd(){
20     ll x=0; bool t=0; char ch=getchar();
21     while (ch<‘0‘ || ch>‘9‘) t|=(ch==‘-‘),ch=getchar();
22     while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
23     if (t) return -x; else return x;
24 }
25
26 bool cmp0(int x,int y){ return L[x]<L[y]; }
27 bool cmp1(int x,int y){ return a[x]>a[y] || (a[x]==a[y] && b[x]>b[y]); }
28 bool cmp2(int a,int b){ return X[a]<X[b]; }
29 bool Cmp(int x,int y,int z){ return ((b[y]-b[x])*(a[y]-a[z])>=(b[z]-b[y])*(a[x]-a[y])); }
30
31 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
32
33 void dfs(int x){ L[x]=++tim; for (int i=h[x]; i; i=nxt[i]) dfs(to[i]); R[x]=tim; }
34
35 void build(int x,int L,int R){
36     ed[x]=-1;
37     if (L==R) { pos[L]=x; return; }
38     int mid=(L+R)>>1; build(lson); build(rson);
39 }
40
41 void ins(int x,int L,int R,int l,int r,int k){
42     if (L==l && r==R){
43         int &j=ed[x];
44         for (; st[x]<j && Cmp(T[x][j-1],T[x][j],k); j--,T[x].pop_back());
45         j++; T[x].push_back(k);
46         return;
47     }
48     int mid=(L+R)>>1;
49     if (r<=mid) ins(lson,l,r,k);
50     else if (l>mid) ins(rson,l,r,k);
51         else ins(lson,l,mid,k),ins(rson,mid+1,r,k);
52 }
53
54 ll que(int k,int x){
55     ll ans=inf;
56     for (int i=pos[k]; i; i>>=1){
57         int &j=st[i];
58         for (; j<ed[i] && (a[T[i][j]]-a[T[i][j+1]])*x>=b[T[i][j+1]]-b[T[i][j]]; j++);
59         if (j<=ed[i]) ans=min(ans,a[T[i][j]]*x+b[T[i][j]]);
60     }
61     return ans;
62 }
63
64 int main(){
65     freopen("travel.in","r",stdin);
66     freopen("travel.out","w",stdout);
67     n=rd(); m=rd(); b[1]=rd(); id[1]=1;
68     rep(i,2,n){
69         op=rd(); fr=rd()+1; d=rd()+1; add(fr,i);
70         if (op) V[d].push_back(i);
71         else id[d]=i,x=rd(),rd(),rd(),c=rd(),a[d]=-2ll*x,b[d]=1ll*x*x+c;
72     }
73     dfs(1); build(1,1,n);
74     rep(i,1,n) q[i]=i; sort(q+1,q+n+1,cmp1);
75     rep(l,1,n) if (id[q[l]]){
76         int i=q[l];
77         sort(V[i].begin(),V[i].end(),cmp0); int k=L[id[i]];
78         for (vector<int>::iterator it=V[i].begin(); it!=V[i].end(); k=R[*it]+1,it++)
79             if (k<L[*it]) ins(1,1,n,k,L[*it]-1,i);
80         if (k<=R[id[i]]) ins(1,1,n,k,R[id[i]],i);
81     }
82     rep(i,1,m) q[i]=i,x=rd(),y=rd(),K[i]=L[x+1],X[i]=y;
83     sort(q+1,q+m+1,cmp2);
84     rep(i,1,m) ans[q[i]]=que(K[q[i]],X[q[i]])+1ll*X[q[i]]*X[q[i]];
85     rep(i,1,m) printf("%lld\n",ans[i]);
86     return 0;
87 }

原文地址:https://www.cnblogs.com/HocRiser/p/8999556.html

时间: 2024-10-07 16:22:04

[CTSC2016]时空旅行(线段树+凸包)的相关文章

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

BZOJ3533 [Sdoi2014]向量集 【线段树 + 凸包 + 三分】

题目链接 BZOJ3533 题解 我们设询问的向量为\((x_0,y_0)\),参与乘积的向量为\((x,y)\) 则有 \[ \begin{aligned} ans &= x_0x + y_0y \y &= -\frac{x_0}{y_0}x + \frac{ans}{y_0} \\end{aligned} \] 所以向量集里的向量实际上可以对应到平面上一组点,我们用一个斜率固定的直线去经过这些点,使得斜率最大或最小 当\(y_0 > 0\)时,要求截距最大 当\(y_0 <

[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\)的,但是因为要

线段树分治总结

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

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

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

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

BZOJ 2402 陶陶的难题II 二分答案+斜率优化+树链剖分+线段树维护凸包

题目大意:给定一棵树,每个点有两个坐标(x1,y1)和(x2,y2),多次询问某条链上选择两个点i和j(可以相同),求(y1i+y2j)/(x1i+x2j)的最大值 我竟没看出来这是01分数规划...真是老了... 二分答案ans,问题转化成验证(y1i+y2j)/(x1i+x2j)是否>=ans 将式子变形可得(y1i-ans*x1i)+(y2j-ans*x2j)>=0 加号两边独立,分别计算即可 问题转化为求链上y-ans*x最大的点 令P=y-ans*x 则y=ans*x+P 我们发现这

[BZOJ 3531][Sdoi2014]旅行(树链剖分+线段树)

Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行.旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿.当然旅程的终点也是信仰与他相同的城市.S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值. 在S国的历史