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\)的,但是因为要答案最小,所以可以按照斜率从大到小(\(x\)从小到大)的顺序插入元素对应的直线,然后按照\(x_0\)从小到大查询,这样由于插入和查询的单调性所以可以少掉一个log

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=5e5+10;
LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
struct node
{
    LL x,y;
}a[N],qr[N];
vector<int> ti[N],e[N];
LL an[N];
int n,m,q,fa[N],ty[N],sz[N],dfn[N],tt,sb[N];
bool cmp1(int aa,int bb){return a[aa].x!=a[bb].x?a[aa].x<a[bb].x:a[aa].x*a[aa].x+a[aa].y>a[bb].x*a[bb].x+a[bb].y;}
bool cmp2(int aa,int bb){return qr[aa].x<qr[bb].x;}
void dfs(int x)
{
    dfn[x]=++tt,sz[x]=1;
    ti[ty[x]].push_back(dfn[x]);
    vector<int>::iterator it;
    for(it=e[x].begin();it!=e[x].end();++it)
    {
        int y=*it;
        dfs(y),sz[x]+=sz[y];
    }
    ti[ty[x]].push_back(dfn[x]+sz[x]);
}
struct line
{
    db k,b;
    line(){}
    line(LL x,LL y){k=-2*x,b=x*x+y;}
};
db jiao(line aa,line bb){return (bb.b-aa.b)/(aa.k-bb.k);}
struct HULL
{
    vector<line> qu;
    int hd,tl;
    HULL(){hd=0,tl=-1;}
    void inst(line aa)
    {
        if(hd<=tl&&qu[tl].k==aa.k) qu.pop_back(),--tl;
        while(hd<tl&&jiao(aa,qu[tl])<=jiao(qu[tl],qu[tl-1])) qu.pop_back(),--tl;
        qu.push_back(aa),++tl;
    }
    LL quer(LL x)
    {
        while(hd<tl&&x>=jiao(qu[hd],qu[hd+1])) ++hd;
        return hd<=tl?(LL)round(qu[hd].k*(db)x+qu[hd].b):1ll<<50;
    }
}hl[N<<2];
#define mid ((l+r)>>1)
int ps[N];
void setli(int o,int l,int r,int ll,int rr,line x)
{
    if(ll<=l&&r<=rr){hl[o].inst(x);return;}
    if(ll<=mid) setli(o<<1,l,mid,ll,rr,x);
    if(rr>mid) setli(o<<1|1,mid+1,r,ll,rr,x);
}
void bui(int o,int l,int r)
{
    if(l==r){ps[l]=o;return;}
    bui(o<<1,l,mid),bui(o<<1|1,mid+1,r);
}

int main()
{
    n=rd(),q=rd();
    a[++m]=(node){0,rd()};
    ty[1]=1;
    for(int i=2;i<=n;++i)
    {
        int op=rd();
        fa[i]=rd()+1;
        int ii=rd()+1;
        e[fa[i]].push_back(i);
        ty[i]=ii;
        m=max(m,ii);
        if(op==0)
        {
            a[ii].x=rd();
            rd(),rd();
            a[ii].y=rd();
        }
    }
    dfs(1);
    for(int i=1;i<=n;++i) sb[i]=i;
    sort(sb+1,sb+n+1,cmp1);
    for(int i=1;i<=n;++i)
    {
        int ii=sb[i],nn=ti[ii].size();
        for(int j=0;j+1<nn;j+=2)
            if(ti[ii][j]<=ti[ii][j+1]-1)
                setli(1,1,n,ti[ii][j],ti[ii][j+1]-1,line(a[ii].x,a[ii].y));
    }
    for(int i=1;i<=q;++i)
    {
        int y=rd()+1,x=rd();
        qr[i]=(node){x,y};
    }
    for(int i=1;i<=q;++i) sb[i]=i;
    sort(sb+1,sb+q+1,cmp2);
    bui(1,1,n);
    for(int i=1;i<=q;++i)
    {
        int ii=sb[i],es=qr[ii].y;
        LL y=qr[ii].x;
        int o=ps[dfn[es]];
        an[ii]=1ll<<50;
        while(o)
        {
            an[ii]=min(an[ii],y*y+hl[o].quer(y));
            o>>=1;
        }
    }
    for(int i=1;i<=q;++i) printf("%lld\n",an[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/smyjr/p/11507520.html

时间: 2024-11-09 06:52:04

luogu P5416 [CTSC2016]时空旅行的相关文章

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

[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)\)个区间 最

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

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

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

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

uoj198【CTSC2016】时空旅行

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

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

luogu题解P2502[HAOI2006]旅行--最小生成树变式

题目链接 https://www.luogu.org/problemnew/show/P2502 分析 一个很\(naive\)的做法是从\(s\)到\(t\)双向BFS这当然会TLE 这时我就有个想法就是二分套二分边下标来求得一个比值,同时排序后从小到大枚举每一条边作为最小值,同时再枚举每一条边,如果边权之比小于比值就连起来用并查集维护连通性,可是这个时间复杂度\(O(m^2 log^2m \ \alpha(n))\)过不去QAQ 然后想为什么不直接枚举每条边作为最小值,同时搞一颗以这条边为最

luogu P1027 Car的旅行路线

题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第I个城市中高速铁路了的单位里程价格为Ti,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为t. 图例(从上而下) 机场 高速铁路 飞机航线 注意:图中并没有 标出所有的铁路与航线. 那么Car应如何安排到城市B的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教. 找出一条从城市A到B的