[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序在线段树上覆盖对应区间并预处理出每个线段树节点的凸壳即可

把询问按$x$排序,那么我们查询时就可以在线段树上利用单调性做到$O(m\log_2n+n)$

总时间复杂度$O(n\log_2^2n+m\log_2n)$

代码准确度++

#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef double du;
const du eps=1e-8;
const ll inf=9223372036854775807ll;
int h[500010],nex[500010],to[500010],in[500010],ou[500010],M;
void add(int a,int b){
	M++;
	to[M]=b;
	nex[M]=h[a];
	h[a]=M;
}
void dfs(int x){
	M++;
	in[x]=M;
	for(int i=h[x];i;i=nex[i])dfs(to[i]);
	ou[x]=M;
}
ll c[500010],x[500010];
int at[500010];
vector<int>pl[2000010],del[500010],tmp;
vector<int>::iterator it;
bool cmp1(int x,int y){return in[x]<in[y];}
bool cmp2(int i,int j){return(x[i]==x[j])?x[i]*x[i]+c[i]>x[j]*x[j]+c[j]:(x[i]<x[j]);}
void modify(int L,int R,int v,int l,int r,int x){
	if(L<=l&&r<=R)return pl[x].push_back(v);
	int mid=(l+r)>>1;
	if(L<=mid)modify(L,R,v,l,mid,x<<1);
	if(mid<R)modify(L,R,v,mid+1,r,x<<1|1);
}
du ins(int i,int j){return(x[i]*x[i]+c[i]-x[j]*x[j]-c[j])/((x[i]-x[j])*2.);}
bool leq(du x,du y){return x-y<eps;}
void dfs(int l,int r,int u){
	sort(pl[u].begin(),pl[u].end(),cmp2);
	tmp.clear();
	for(it=pl[u].begin();it!=pl[u].end();it++){
		while(!tmp.empty()){
			if(x[*tmp.rbegin()]==x[*it]){
				tmp.pop_back();
				continue;
			}
			if(tmp.size()==1)break;
			if(leq(ins(*tmp.rbegin(),*it),ins(tmp[tmp.size()-2],*it)))
				tmp.pop_back();
			else
				break;
		}
		tmp.push_back(*it);
	}
	pl[u]=tmp;
	if(l==r)return;
	int mid=(l+r)>>1;
	dfs(l,mid,u<<1);
	dfs(mid+1,r,u<<1|1);
}
struct ask{
	int s,id;
	ll x;
}q[500010];
ll ans[500010];
int head[2000010];
bool cmp3(ask a,ask b){return a.x<b.x;}
ll calc(int i,ll x0){return-2ll*x[i]*x0+x[i]*x[i]+c[i];}
ll query(int u,ll x0,int l,int r,int x){
	ll ans=inf;
	if(!pl[x].empty()){
		while(head[x]<(int)pl[x].size()-1&&leq(ins(pl[x][head[x]],pl[x][head[x]+1]),x0))head[x]++;
		ans=min(ans,calc(pl[x][head[x]],x0));
	}
	if(l==r)return ans;
	int mid=(l+r)>>1;
	if(u<=mid)
		ans=min(ans,query(u,x0,l,mid,x<<1));
	else
		ans=min(ans,query(u,x0,mid+1,r,x<<1|1));
	return ans;
}
int main(){
	int n,m,i,op,fr,id,y,z;
	scanf("%d%d%lld",&n,&m,c+1);
	at[1]=1;
	for(i=2;i<=n;i++){
		scanf("%d%d%d",&op,&fr,&id);
		fr++;
		id++;
		add(fr,i);
		if(op==0){
			scanf("%lld%d%d%lld",x+id,&y,&z,c+id);
			at[id]=i;
		}else
			del[id].push_back(i);
	}
	M=0;
	dfs(1);
	for(i=1;i<=n;i++){
		if(at[i]){
			sort(del[i].begin(),del[i].end(),cmp1);
			y=in[at[i]];
			for(it=del[i].begin();it!=del[i].end();it++){
				if(y<in[*it])modify(y,in[*it]-1,i,1,n,1);
				y=ou[*it]+1;
			}
			if(y<=ou[at[i]])modify(y,ou[at[i]],i,1,n,1);
		}
	}
	dfs(1,n,1);
	for(i=1;i<=m;i++){
		scanf("%d%lld",&q[i].s,&q[i].x);
		q[i].s++;
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp3);
	for(i=1;i<=m;i++)ans[q[i].id]=query(in[q[i].s],q[i].x,1,n,1)+q[i].x*q[i].x;
	for(i=1;i<=m;i++)printf("%lld\n",ans[i]);
}

原文地址:https://www.cnblogs.com/jefflyy/p/9128229.html

时间: 2024-11-09 06:56:55

[UOJ198]时空旅行的相关文章

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

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

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

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

uoj198【CTSC2016】时空旅行

传送门:http://uoj.ac/problem/198 [题解] 首先y.z是没有用的.. 然后式子就是w = (x0-xi)^2+ci的最小值,化出来可以变成一个直线的形式. 然后我们可以用线段树维护dfs序上的每个点. 每个点维护经过这个点的所有直线(标记永久化),也就是维护上凸壳. 然后我们把询问按照x排序,每次决策点只会后移.所以复杂度就有保证啦! 真**难写 还有一个十分有趣的事实啊 我用一个号交完ac在另一个号再交就RE了啊... 不管了反正过了 # include <vecto

[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

poj 3259 Wormholes (负权最短路,SPAF)

Wormholes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 36641   Accepted: 13405 Description While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way p

前端学习总结

前前后后,从对前端一无所知开始自学三个月,到如今作为前端开发工程师工作了三个月,这总共半年的时间里,我学到了什么,怎么学的. 一.前言 1.为什么要学前端开发? 我本来是想学java的,买了一堆java书籍,但怎么变成了学前端,已经无从根究,我自己也是没答案.不过既然已经走上了这条路,而且前端还算有趣,那就接着走下去吧.一个优秀的程序员不会只懂一门语言,因此,java什么的,早晚会接触到.也许,那不是java,而会是Haskell.Phython等任何编程语言. 2.前端开发是做什么的? 老实说