首页 > 其他分享 >P5416 [CTSC2016]时空旅行 题解

P5416 [CTSC2016]时空旅行 题解

时间:2023-02-24 13:55:18浏览次数:54  
标签:cnt name int 题解 ll mid tim P5416 CTSC2016

首先题中的 \(y,z\) 好像没啥用……

首先对于每一次询问,要求 \(\min((x_0-x_i)^2+c_i)\)

设 \((x_0-x_i)^2+c_i=ans\)。

那么 \(x_0^2+x_i^2-2x_0x_i+c_i=ans\)

所以 \(x_0^2+x_i^2+c_i=2x_0x_i+ans\)。

这个式子明显为 \(y=kx+b\) 的形式,所以可以维护下凸包求解。

那么每个点的坐标即为 \((x_i,x_i^2+c_i)\),每个询问的斜率为 \(2x_0\),然后对于每个区间二分求解。

但是有时间变化,想到线段树分治。

首先所有的时间节点成一个树形结构,那么我们可以对这棵树遍历一遍,用 dfs 序对线段树编号。

对于一个点加入,那么会从这个节点开始,到遍历整棵子树完结束,那么对整棵子树加入一个节点。

如果删除一个点,从这个节点开始,到整棵子树完结束,不进行加操作,遍历完子树后,进行加操作。

就如同加上 \((l_1,r_1)\),减去 \((l_2,r_2)\),就等同于加上 \((l_1,l_2-1),(r_1+1,r_2)\) 两个区间。

先将每个点按 \(x\) 为第一关键字从小到大排序,\(y\) 为第二关键字从大到小排序,然后再求解。

时间复杂度:\(O(n\times \log_2^2(n))\)。

考虑优化。

将询问按照斜率从小到大排序,然后对于线段树每个节点间的斜率是有单调性的,记录上次询问得到的下标,然后下次询问就可以直接从这个地方开始。

几个细节:

1、不能全开 long long,会 MLE。

2、\(x\) 坐标相同时要处理一下。

3、注意询问中的时间与线段树中重新编号的区间不同。

时间复杂度:\(O(n\times\log(n))\)。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define pii pair<int,int> 
using namespace std;
const int N=5e5+5;
int n,m,tim=-1,nl,nr,dfn[N];
ll ans[N],k,res;
bool ss[N];
struct node
{
	int name;
	ll x,y;
}a[N];
struct segmentree
{
	vector<int>t;
	int cnt,to;
}f[4*N];
struct ask
{
	int name,tim;
	ll k;
}q[N];
bool cmp(node x,node y)
{
	if(x.x==y.x)return x.x*x.x+x.y>y.x*y.x+y.y;
	return x.x<y.x;
}
bool cmp2(ask x,ask y)
{
	return x.k<y.k;
}
vector<pii>s[N];
vector<int>fr[N],to[N];
void dfs(int x,int name)
{
	dfn[x]=++tim;
	if(ss[x])fr[name].push_back(tim);
	if(!ss[x])to[name].push_back(tim-1);
	int len=s[x].size();
	for(int i=0;i<len;i++)dfs(s[x][i].first,s[x][i].second);
	if(ss[x])to[name].push_back(tim);
	if(!ss[x])fr[name].push_back(tim+1);
}
double clac(int x,int y)
{
	if(a[x].x==a[y].x)return -1e18;
	return 1.0*(a[x].x*a[x].x+a[x].y-a[y].x*a[y].x-a[y].y)/1.0/(a[x].x-a[y].x);
}
ll clac9(ll x)
{
	return x*x;
}
inline int ls(int x)
{
	return x<<1;
}
inline int rs(int x)
{
	return x<<1|1;
}
void build(int x,int l,int r)
{
	f[x].cnt=-1;
	f[x].to=0;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(ls(x),l,mid);
	build(rs(x),mid+1,r);
}
void update(int x,int l,int r)
{
	if(l>=nl&&r<=nr)
	{
		while(f[x].cnt>0&&clac(f[x].t[f[x].cnt],f[x].t[f[x].cnt-1])>=clac(f[x].t[f[x].cnt],k))f[x].cnt--;
		int len=f[x].t.size();
		if(len-1==f[x].cnt)f[x].t.push_back(k),f[x].cnt++;
		else f[x].t[++f[x].cnt]=k;
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=nl)update(ls(x),l,mid);
	if(mid<nr)update(rs(x),mid+1,r);
}
void search(int x,int l,int r)
{
	for(;f[x].to<f[x].cnt;f[x].to++)
		if(clac(f[x].t[f[x].to],f[x].t[f[x].to+1])>=k)break;
	if(f[x].cnt!=-1)res=min(res,clac9(k/2-a[f[x].t[f[x].to]].x)+a[f[x].t[f[x].to]].y);
	if(l==r)return;
	int mid=(l+r)>>1;
	if(mid>=nl)search(ls(x),l,mid);
	else search(rs(x),mid+1,r);
}
signed main()
{
	scanf("%d%d%lld",&n,&m,&a[0].y);
	for(int i=0;i<n;i++)a[i].name=-1;
	a[0].name=0;
	a[0].x=0;
	ss[0]=1;
	for(int i=1;i<=n-1;i++)
	{
		int opt;
		scanf("%d",&opt);
		if(opt==0)
		{
			int ul1,ul2,fa,name;
			scanf("%d%d",&fa,&name);
			scanf("%lld%d%d%lld",&a[name].x,&ul1,&ul2,&a[name].y);
			a[name].name=name;
			s[fa].push_back({i,name});
			ss[i]=1;
		}
		else
		{
			int fa,name;
			scanf("%d%d",&fa,&name);
			s[fa].push_back({i,name});
		}
	}
	dfs(0,0);
	sort(a,a+n,cmp);
	build(1,0,n-1);
	for(int i=0;i<n;i++)
	{
		int x=a[i].name;
		if(x==-1)continue;
		int len=fr[x].size();
		for(int j=0;j<len;j++)
		{
			nl=fr[x][j],nr=to[x][j],k=i;
			if(nr<nl)continue;
			update(1,0,n-1);
		}
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%lld",&q[i].tim,&q[i].k);
		q[i].name=i;
	}
	sort(q+1,q+1+m,cmp2);
	for(int i=1;i<=m;i++)
	{
		nl=dfn[q[i].tim],k=2*q[i].k;
		res=1e18;
		search(1,0,n-1);
		ans[q[i].name]=res;
	}
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}

标签:cnt,name,int,题解,ll,mid,tim,P5416,CTSC2016
From: https://www.cnblogs.com/gmtfff/p/p5416.html

相关文章

  • P7862 [COCI2015-2016#2] DRŽAVA 题解
    适当进行骗分是真的有用。\(40pts\):对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。设\(dp_{i,j}\)代表以\(i\)为编号的一......
  • P1379 八数码难题 题解
    IDA*练习题由于题目问最小步数,很好想到可以用迭代式加深搜索,或是广搜,这里用的是深搜。枚举每次搜索的深度,也就是移动的步数,然后正常深搜,若达到目标解,返回\(\text{ture}......
  • P3213 [HNOI2011]勾股定理 题解
    据说是NP问题。很明显我们要先预处理出来勾股数对。但由于数过于大,所以常规的枚举是解决不了问题的。但也貌似没有什么很好的办法可以立马找到一个数的勾股数对。所以......
  • P3989 [SHOI2013]阶乘字符串 题解
    由于一些不可抗拒的原因,\(n\ge22\)无解。那么只用考虑\(n\le21\)的情况即可。由于\(n\)的范围缩小,导致状压又可以重新使用,所以考虑状压。设\(f_i\)为\(i\)中......
  • AT655 玉座の間 题解
    首先我们要学习一下费用流。费用流是什么呢,可以理解为边带权值的网络流。那么最小费用最大流,是指在满足最大流的情况下的最小费用。那么我们就要实现这个过程。首先对......
  • P3997 [SHOI2013]扇形面积并 题解
    理解题意后可以把题目看成一个覆盖线段的问题。对于点在\(-m\)上,看成在\(m\)上。对于\(l<r\),不用处理。对于\(l>r\),将问题看成\((l,m)\)和\((-m+1.r)\)两个区......
  • P5616 [MtOI2019]恶魔之树 题解
    期望就是来搞笑的。由于有最小公倍数,所以可以想到分解质因数,对于多个数求最小公倍数,取每个质因子的最大指数,最后相乘即可。既然都知道了这个,那么就想到先统计每个数的个......
  • CF10E Greedy Change 题解
    一个非常离谱的题。首先有结论,如果有\(w\)使贪心不为最优解,那么比\(w\)小的第一个\(a_i\),用贪心法求面值为\(a_i-1\),除了最后选的一个数\(a_j\)会比原方法多选一......
  • P2161 [SHOI2009]会场预约 题解
    没事打了个Splay,然后调了3h。觉得题解的找前驱后继与删除复杂了点,主要讲一下这的思路。由于平衡树中每一个点代表的区间互不相交,所有平衡树满足\(l,r\)两个值的BST。......
  • P3224 [HNOI2012]永无乡 题解
    典型Splay练习题。开始建\(n\)个Splay,每一次建边用并查集判断是否在一个子图,不在就合并,即把一个Splay的所有点全插入到另一个Splay中,需要合并的点可以用vector存储。但......