Loading

[CTSC2016]时空旅行(斜率优化+线段树分治)

洛谷题目传送门

解题思路

首先发现只有\(x\)\(c\)是有用的
这些时空构成了一棵树,我们实际上要找一个点\(i\),满足对于给出的\(X\)
\(min((X-x_i)^2+c_i)\)
根据斜率优化的套路
我们展开式子
\(ans=(X-x_i)^2+c_i=X^2-2x_iX+x_i^2+c_i\)
\(x_i^2+c_i=ans+2*x_i*X-X^2\)
那么可以把每个星球看成一个点\((2\times x_i,x_i^2+c_i)\)然后用一条斜率为\(X\)的凸包截这些点,所能获得的最大截距
我们只需要维护一个上凸壳就行了
然后我们就得到了一个思路
我们建立一颗以树的\(dfs\)为序列的线段树,这样每个星球存在的范围是若干区间,并对于每个区间都维护一个上凸壳,然后为了更方便的建立凸包,我们先把\(x\)坐标排序,然后再把每个星球插入到它在线段树上存在的区间里
查询的时候如果在线的话每次可能吧整个凸包遍历一遍
所以我们把这些询问按照询问的斜率排序,然后对于每个节点记录一个\(cur[x]\)表示当前扫描到了这个凸包的那个点,因为排过序了所以不可能访问之前的点,也就是说最多访问整个树的凸包一遍,而每个点最多在\(O(logn)\)个区间里,所以我们的复杂度为\(O(nlogn)\)
对于怎么处理每个星球在线段树上的区间,可以这样考虑
首先对于\(dfs\)序来说,第一个有这个星球的节点就是这个星球出现的位置
所以星球只可能在这个节点的子树内出现了
然后之后每次出现的时候,就代表删除,所以这个点的子树内的点都没有这个星球,记录一下上一次出现的位置,那么当前区间就是\([ed_{last}+1,dfn_i]\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5+7;
typedef double db;
const LL INF = 1e18+7;
inline LL read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
int n,m;
LL C[N],X[N];
int note[N];
struct edge
{
	int y,next;
}e[2*N];
struct point
{
	LL x,y;
}p[N];
struct Query
{
	int X,s,id;
}Q[N];
point Point(LL x,LL y)
{
	return (point){x,y};
}
int link[N],t=0;
void add(int x,int y)
{
	e[++t].y=y;
	e[t].next=link[x];
	link[x]=t;
}
int dfn[N],End[N],tot=0;
vector<int> G[N];
int id[N];
void dfs(int x)
{
	dfn[x]=++tot;
	G[note[x]].push_back(x);
	for(int i=link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		dfs(y);
	}
	End[x]=tot;
}
bool cmp(int i,int j)
{
	if(X[i]==X[j]) return C[i]>C[j];
	return X[i]<X[j];
}
bool cmp2(Query a,Query b)
{
	return a.X<b.X;
}
vector<int> A[N*4];
db slope(int i,int j)
{
	return (db)(p[j].y-p[i].y)/(p[j].x-p[i].x);
}
void Add(int k,int i)
{
	int sz=A[k].size();
	while(sz>1&&(p[A[k][sz-1]].y-p[A[k][sz-2]].y)*(p[i].x-p[A[k][sz-1]].x)>=(p[i].y-p[A[k][sz-1]].y)*(p[A[k][sz-1]].x-p[A[k][sz-2]].x))
	{
		sz--;
		A[k].pop_back();
	}
	A[k].push_back(i);
	return;
}
void Insert(int k,int l,int r,int L,int R,int i)
{
	if(L<=l&&r<=R)
	{
		Add(k,i);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) Insert(k<<1,l,mid,L,R,i);
	if(R>mid) Insert(k<<1|1,mid+1,r,L,R,i);
}
int cur[N*4];
LL Ask(int k,int l,int r,int x,int K)
{
	LL res=INF;
	int sz=A[k].size();
	if(sz)
	{
		while(cur[k]<sz-1&&p[A[k][cur[k]+1]].y-p[A[k][cur[k]]].y<1ll*K*(p[A[k][cur[k]+1]].x-p[A[k][cur[k]]].x)) 
		{
			++cur[k];
		}
		res=p[A[k][cur[k]]].y-p[A[k][cur[k]]].x*K+1ll*K*K;
	}
	if(l==r) return res;
	int mid=(l+r)>>1;
	if (x<=mid) return min(res,Ask(k<<1,l,mid,x,K));
	else return min(res,Ask(k<<1|1,mid+1,r,x,K));
}
void Build()
{
	for(int i=1;i<=n;i++)
	{
		int x=id[i];
		if(!G[x].empty())
		{
			p[i]=Point(2ll*X[x],1ll*X[x]*X[x]+C[x]);
			int lst=dfn[G[x][0]];
			int siz=G[x].size();
			for(int j=1;j<siz;j++)
			{
				int y=G[x][j];
				Insert(1,1,n,lst,dfn[y]-1,i);
				lst=End[y]+1;
			}
			if(lst<=End[G[x][0]]) Insert(1,1,n,lst,End[G[x][0]],i);
		}
	}
}
LL Ans[N];
int main()
{
	n=read();
	m=read();
	C[1]=read();
	note[1]=1;
	for(int i=2;i<=n;i++)
	{
		int opt=read(),ex;
		if(opt==0)
		{
			int Fa=read()+1,id=read()+1;
			X[id]=read();
			ex=read();ex=read();
			C[id]=read();
			add(Fa,i);
			note[i]=id;
		}
		else
		{
			int Fa=read()+1,id=read()+1;
			add(Fa,i);
			note[i]=id;
		}
	}
	dfs(1);
	for(int i=1;i<=n;i++) id[i]=i;
	sort(id+1,id+n+1,cmp);
	Build();
	for(int i=1;i<=m;i++)
	{
		int s=read()+1,x=read();
		Q[i]=(Query){x,dfn[s],i};
	}
	sort(Q+1,Q+m+1,cmp2);
	for(int i=1;i<=m;i++)
	Ans[Q[i].id]=Ask(1,1,n,Q[i].s,Q[i].X);
	for(int i=1;i<=m;i++)
	printf("%lld\n",Ans[i]);
	return 0;
}
posted @ 2021-12-29 10:39  Larunatrecy  阅读(51)  评论(0)    收藏  举报