时空旅行[线段树分治][维护凸壳]

前言

肝了一上午…这是我才学线段树分治的例题…真舒服

题目

温馨提示:首先在UOJ做,LOJ挖数据,BZOJ终极评测。。。
UOJ198
在这里插入图片描述
二手剽…

思路

为什么不能用 C D Q CDQ CDQ 分治做?因为无法减去一个操作的影响(一些计数类型的就可以),或者说信息没有可减性,无法进行转化
考虑线段树合并,如何转化为一个星球的作用时间?
这里的’时间’并不是一个具体的时间,而是 d f s dfs dfs
所有版本构成一个树形结构,那么对于一个星球而言,一次加操作就是在 [ d f n u , d f n u + s i z u − 1 ] [dfn_u,dfn_u+siz_u-1] [dfnu,dfnu+sizu1] 的区间加,删除同理
但是实际过程中因为题目输入比较特殊,所以可以通过 D F S DFS DFS 时后一些小操作避免删除(不是重点)
然后通过将修改操作转化为 O ( n ) O(n) O(n) 段连续的,分在 O ( n l o g n ) O(nlogn) O(nlogn) 个线段树节点上
现在就是区间修改,一些单点询问,并且单先询问带限制(即你是拿着 x 0 x_0 x0 去询问的)
考虑处理限制:

m i n { ( x − x 0 ) 2 + c } min\{(x-x_0)^2+c\} min{(xx0)2+c} 平方项的出现优先考虑斜率凸包相关
m i n { ( x − x 0 ) 2 + c } = m i n { − 2 x 0 x + x 2 + c } + x 0 2 min\{(x-x_0)^2+c\}=min\{-2 x_0x+x^2+c\}+x_0^2 min{(xx0)2+c}=min{2x0x+x2+c}+x02
b = − 2 x 0 x i + x i 2 + c i b=-2x_0x_i+x_i^2+c_i b=2x0xi+xi2+ci
2 x 0 x i + b = x i 2 + c i 2x_0x_i+b=x_i^2+c_i 2x0xi+b=xi2+ci ,求 b m i n b_{min} bmin

将每个星球看作 ( x i , x i 2 + c i ) (x_i,x_i^2+c_i) (xi,xi2+ci) 即是求以 k = 2 x 0 k=2x_0 k=2x0 过点的最小截距,维护下凸壳

由于线段树分治已经将修改覆盖相关询问,就能改变一个节点内修改操作的顺序
我选择修改按斜率递减排序,坐标递增排序,单调栈维护就能做到均摊 O ( n l o g n ) O(nlogn) O(nlogn)
所以总的时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
一些细节:
对于横坐标 x i x_i xi 相等的点我们取 y i y_i yi 最小的,返回斜率是注意判断
一些问题:
1.BZOJ老化后我卡常卡得丧心病狂
2.还可以用凸包优化…

代码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("inline", "no-stack-protector", "unroll-loops")
#pragma GCC diagnostic error "-fwhole-program"
#pragma GCC diagnostic error "-fcse-skip-blocks"
#pragma GCC diagnostic error "-funsafe-loop-optimizations"
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline LL read() {
	bool f=0;LL x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 500000
#define INF 100000000000000ll
struct node{
	int type,id;
}a[MAXN+5];
struct Modify{
	int L,R;LL x,y;
	friend bool operator < (Modify a,Modify b){return a.x<b.x;}
}M[MAXN+5];
struct Ask{
	int id,t,x0;
	friend bool operator < (Ask a,Ask b){return a.x0>b.x0;}
}Q[MAXN+5];
int cntm;
LL x[MAXN+5],c[MAXN+5];
int cnt_dfn,dfn[MAXN+5],siz[MAXN+5];
struct Node{
	struct Edge{
		int nxt,v;
	}edge[2*MAXN+5];
	int ecnt,head[MAXN+5];
	void Addedge(int u,int v){
		edge[++ecnt]=(Edge){head[u],v},head[u]=ecnt;
		return ;
	}
}G,ML,MR;
inline void DFS(register int u){
	siz[u]=1,dfn[u]=++cnt_dfn;
	if(!a[u].type) ML.Addedge(a[u].id,dfn[u]);
	else MR.Addedge(a[u].id,dfn[u]-1);
	for(register int i=G.head[u];i;i=G.edge[i].nxt){
		register int v=G.edge[i].v;
		DFS(v),siz[u]+=siz[v];
	}
	if(!a[u].type) MR.Addedge(a[u].id,dfn[u]+siz[u]-1);
	else ML.Addedge(a[u].id,dfn[u]+siz[u]);
	return ;
}
#define lch (u<<1)
#define rch (u<<1|1)
int siz_Stk[4*MAXN+5];
vector<int> Stk[4*MAXN+5];
inline LL Y(register int i,register int j){return M[i].y-M[j].y;}
inline LL X(register int i,register int j){return M[i].x-M[j].x;}
double K(int i,int j){
	if(M[i].x==M[j].x)return M[i].y>M[j].y?INF:-INF;
	return 1.0*(Y(i,j))/(X(i,j));
}
inline void Insert_M(register int u,register int L,register int R,register int qL,register int qR,register int id){
	if(qL<=L&&R<=qR){
		register int tp=siz_Stk[u]-1;
		while(tp>0&&Y(id,Stk[u][tp])*X(Stk[u][tp],Stk[u][tp-1])<=Y(Stk[u][tp],Stk[u][tp-1])*X(id,Stk[u][tp]))
		//while(tp>0&&K(id,Stk[u][tp])<=K(Stk[u][tp],Stk[u][tp-1]))
			tp--,Stk[u].pop_back(),siz_Stk[u]--;
		Stk[u].push_back(id),siz_Stk[u]++;
		return ;
	}
	register int Mid=(L+R)>>1;
	if(qL<=Mid)
		Insert_M(lch,L,Mid,qL,qR,id);
	if(Mid+1<=qR)
		Insert_M(rch,Mid+1,R,qL,qR,id);
	return ;
}
LL ans[MAXN+5];
inline LL Query(int u,int L,int R,int p,LL x0){
	int tp=siz_Stk[u]-1;
	register LL ret=INF;
	while(tp>0&&2*x0*X(Stk[u][tp],Stk[u][tp-1])<=Y(Stk[u][tp],Stk[u][tp-1]))
	//while(tp>0&&2*x0<=K(Stk[u][tp],Stk[u][tp-1]))
		tp--,Stk[u].pop_back(),siz_Stk[u]--;
	if(tp!=-1)
		ret=M[Stk[u][tp]].y-2*x0*M[Stk[u][tp]].x+x0*x0;
	if(L==R) return ret;
	register int Mid=(L+R)>>1;
	if(p<=Mid)
		ret=min(ret,Query(lch,L,Mid,p,x0));
	else ret=min(ret,Query(rch,Mid+1,R,p,x0));
	return ret;
}
int main(){
	int n=read(),m=read();
	x[1]=0,c[1]=read();
	a[1]=(node){0,1};
	for(int i=2;i<=n;i++){
		int opt=read();
		int fr=read()+1,id=read()+1;
		G.Addedge(fr,i);
		if(opt==0)
			x[id]=read(),read(),read(),c[id]=read();
		a[i]=(node){opt,id};
	}
	DFS(1);
	//puts("");
	for(register int i=1;i<=n;i++)
		for(register int j1=ML.head[i],j2=MR.head[i];j1;j1=ML.edge[j1].nxt,j2=MR.edge[j2].nxt){
			//printf("%d %d\n",ML.edge[j1].v,ML.edge[j2].v);
			M[++cntm]=(Modify){ML.edge[j1].v,MR.edge[j2].v,x[i],x[i]*x[i]+c[i]};
		}
	sort(M+1,M+cntm+1);
	for(int i=1;i<=cntm;i++)
		Insert_M(1,1,n,M[i].L,M[i].R,i);
	for(int i=1;i<=m;i++){
		int t=read()+1,x0=read();
		Q[i]=(Ask){i,t,x0};
	}
	sort(Q+1,Q+m+1);
	for(int i=1;i<=m;i++)
		ans[Q[i].id]=Query(1,1,n,dfn[Q[i].t],Q[i].x0);
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值