[CTSC2016]时空旅行

一、题目

点此看题

二、解法

可以看出这些平行时空呈树形结构,每个星球都会出现在一段连续的 d f n dfn dfn序中,而我们正是要对每个时空的星球中找最小值,那么可以把星球打在 d f n dfn dfn的线段树上,使用线段树分治。

考虑怎么从若干个点中选取最优解,首先 y , z y,z y,z没有卵用,发现是平方的柿子,考虑斜率优化, x [ j ] > x [ k ] x[j]>x[k] x[j]>x[k] j j j更优的条件:
( X − x j ) 2 + c j < ( X − x k ) 2 + c k (X-x_j)^2+c_j<(X-x_k)^2+c_k (Xxj)2+cj<(Xxk)2+ck推出来是这个样子:
x j 2 + c j − x k 2 − c k x j − x k < 2 X \frac{x_j^2+c_j-x_k^2-c_k}{x_j-x_k}<2X xjxkxj2+cjxk2ck<2X画画图可以发现需要维护斜率单调递增的凸包,然后我们先按 x x x给每个星球排序,然后就可以插入到线段树里去了,每个点用 v e c t o r vector vector维护凸包。询问按 x x x排序,这样就可以直接弹出对首,每个询问单独处理,从根跑到叶子,每次更新一下最小值即可。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define ll long long
const int M = 500005;
const ll inf = (1ll<<60);
int read()
{
    int x=0,flag=1;char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,tot,cnt,a[M],dfn[M],to[M],f[M],h[4*M],t[4*M];
ll tmp,ans[M],c[M],val[M];
vector<int> al[M],ar[M],q[4*M];
struct edge
{
    int v,next;
    edge(int V=0,int N=0) : v(V) , next(N) {}    
}e[2*M];
struct node
{
	int x,y,id;
	bool operator < (const node &b) const
	{
		return x<b.x;
	}
}p[M];
bool cmp1(int x,int y)
{
	return val[x]<val[y];
}
void dfs(int u,int fa)
{
    dfn[u]=++cnt;
    if(to[u]>0) al[to[u]].push_back(cnt);
    if(to[u]<0) ar[-to[u]].push_back(cnt-1);
    for(int i=f[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa) continue;
        dfs(v,u);
    }
    if(to[u]>0) ar[to[u]].push_back(cnt);
    if(to[u]<0) al[-to[u]].push_back(cnt+1);
}
double kk(int j,int k)
{
	return (double)((double)val[j]*val[j]+c[j]-val[k]*val[k]-c[k])/((double)val[j]-val[k]);
}
void ins(int i,int l,int r,int L,int R,int x)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		while(q[i].size()<=t[i]+5) q[i].push_back(0);
		while(h[i]<=t[i] && val[q[i][t[i]]]==val[x])
		{
			if(c[q[i][t[i]]]>=c[x]) t[i]--;
			else break;
		}
		while(h[i]<t[i] && kk(x,q[i][t[i]])<kk(q[i][t[i]],q[i][t[i]-1])) t[i]--;
		q[i][++t[i]]=x;
		return ;
	}
	int mid=(l+r)>>1;
	ins(i<<1,l,mid,L,R,x);
	ins(i<<1|1,mid+1,r,L,R,x);
}
void ask(int i,int l,int r,int pos,ll x)
{
	while(h[i]<t[i] && kk(q[i][h[i]+1],q[i][h[i]])<=2.0*x) h[i]++;
	if(h[i]<=t[i]) tmp=min(tmp,(x-val[q[i][h[i]]])*(x-val[q[i][h[i]]])+c[q[i][h[i]]]);
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(mid>=pos) ask(i<<1,l,mid,pos,x);
	else ask(i<<1|1,mid+1,r,pos,x);
}
signed main()
{
    n=read();m=read();scanf("%lld",&c[0]);
    for(int i=1;i<n;i++)
    {
        int op=read();
        if(op==0)
        {
            int u=read(),x=read();
            to[i]=x;
            scanf("%lld",&val[x]);read();read();scanf("%lld",&c[x]);
            e[++tot]=edge(i,f[u]),f[u]=tot;
            e[++tot]=edge(u,f[i]),f[i]=tot;
        }
        else
        {
            int u=read(),x=read();
            to[i]=-x;
            e[++tot]=edge(i,f[u]),f[u]=tot;
            e[++tot]=edge(u,f[i]),f[i]=tot;
        }
    }
    dfs(0,0);
    for(int i=1;i<=4*n;i++) h[i]=0,t[i]=-1;
    for(int i=1;i<=n;i++) a[i]=i;
    ins(1,1,n,1,n,0);
    sort(a+1,a+1+n,cmp1);
    for(int i=1;i<=n;i++)
    {
    	int x=a[i];
    	for(int j=0;j<al[x].size();j++)
    	{
    		int LL=al[x][j],RR=ar[x][j];
    		ins(1,1,n,LL,RR,x);
		}
	}
	for(int i=1;i<=m;i++)
	{
		int y=read(),x=read();
		p[i]=node{x,y,i};
	}
	sort(p+1,p+1+m);
	for(int i=1;i<=m;i++)
	{
		tmp=inf;
		ask(1,1,n,dfn[p[i].y],p[i].x);
		ans[p[i].id]=tmp; 
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值