UOJ#198 [CTSC2016]时空旅行

4 篇文章 0 订阅
3 篇文章 0 订阅

题目

UOJ#198 [CTSC2016]时空旅行

题解

QAQ允许我做一个悲伤的表情。这题题解真少。我要做一个造福人类的人。



首先呢可以看出,如果把每个时空当做一个节点,这是棵树,每个节点有一个添加或者删除星球的标记(也许是一种高超的差分方法?(雾))

然后看完样例就知道y,z坐标是拿来搞笑的。

所以其实这是个一维上的问题。从一堆点中选一个出来。最小化 (xix0)2+ci x0 是询问位置。

啊这个x如果固定的话,主席树搞一搞然后区间min嘛qwq。
然后发现了不放弃星球这种条件,也就是说,加入的星球一定会对后面造成影响,加上树是一条链这个特殊情况,就是个修改+询问的模式。让窝想起了cdq。窝门来考虑cdq怎么做(如果没有往cdq想直接想出了凸包可以跳过看下一段qwq本蒟蒻的思维比较僵硬)因为加入的点一定会对后面的点产生贡献,所以我们还是拿个数据结构维护左边的加入星球操作,然后更新右边的询问。做cdq的时候要先分治然后按x排序,从左往右做一次还得从右往左做一次。这个时候发现,因为每个星球的c固定,那假如我从左往右的时候,如果有 xi<xj 那么i星球一定会在某个时候被j艹翻然后从此以后不用考虑i,因为d^2的导数是2d和d正相关的呀。于是我们就发现每个星球其实控制的是x轴上一段区域,然后单调栈维护(比如说在t点被艹翻,如果 t<=xj 就把i弹掉)。啊这就是窝所想的了qwq。没有写并不知道正确性。

然后这一段是衔接。拆一拆式子 cost=(xix0)2+ci=x2i2xix0+x20+ci x0 确定位置之后是固定的, x2i+ci 是星球自身属性,所以应该最小化 2xix0+x2i+ci x0 是变量,这就是个一次函数呀。 k=2xi,b=x2i+ci 所以刚才那个单调栈,实际上应该是个凸包。

然后再来观察一波得到,每个节点里的星球可以通过从这个节点爬到根节点的路径上的信息来确定(很显然的qwq)。然后其实就是要维护这些节点的凸包了。

树上到根节点信息?你想到了啥。dfs序+线段树。

一个加星球操作就会影响一棵子树。采用标记永久化的思想,并在线段树每个节点保存一个凸包。(因为标记下放会涉及删除,很麻烦)。(把地球另外考虑不加进去)

既然删除很麻烦,那题目中的删除怎么做呢?假如我在 u 这个节点加了一个x星球,那么应该在[L[u],R[u]]这个区间修改,如果我在 v 这个节点把x删了(注意v一定是u的后代,而且除了u的节点的加操作不可能加的是x星球,这个是题意保证),那么我应该在 [L[v],R[v]] 做删除操作,可是我们做不了删除操作233333,那咋办,我拆嘛,我拆成俩区间 [L[u],L[v]1] [R[v]+1,R[u]] 不就好了?总的区间个数还是O(n)级别的(好像只会<=n),当然拆的时候还要一些特判,这里只说思想。

啊然后解决了加入删除操作,最重要的还是回答询问啊(没询问做个卵xxx)。

等着,只解决了区间问题还没解决咋维护凸包呢。(我跟你说你可以每个节点搞个splay然后动态加nlogn^2xxxx),因为先加哪个星球无所谓嘛,学过半平面交的小伙伴都知道,排序了之后O(n)单调栈能做(和之前cdq部分的想法差不多的)。那我先把星球按斜率排序(怪怪的,不过因为星球对应直线嘛)然后用半平面交的方法。每个节点保存一个单调栈,当一个区间被修改操作完全包含的时候,就在这个单调栈里弹弹弹就ok。O(nlogn)

怎么回答询问呢。

巧啊,询问可以离线的。想一下,在一个凸包上,多次问你某个x对应的是单调栈里哪条线段,怎么做?(每次去二分,nlog^2xxxxxx)肯定是把询问按x排了序然后在单调栈里挨着扫一遍呀。这样O(nlogn+n)了。

那线段树上怎么做啊。还是先把询问排序,来我们想一下每个节点有个pos表示上一次最小值取到的位置(初始是0),如果有询问问到这里,我就尝试往后挪,能挪又不会越界的时候就挪就好了。复杂度是 O(nlogn+nlogn)的.然后挨着挨着问就好了。

最后答案记得要跟地球的答案取个min。

啊终于写完了,好长一篇.

代码

我在UOJ用scanf交的话50w的时候会RE,好迷(扶额)

//QWsin
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=500000+10;
const int maxm=500000+10;
inline int read(){
    int ret=0,ok=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')ok=-1;ch=getchar();}
    for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
    return ret*ok;
}

ll c0;
int n,m,add[maxn];//add[i]表示第i个星球在哪个时空被加入 

int first[maxn],Next[maxm],tmp[maxn],ecnt;
struct Edge{int u,v;Edge(int u=0,int v=0):u(u),v(v){}}e[maxm];
inline void add_edge(int u,int v){
    Next[ecnt]=first[u];first[u]=ecnt;e[ecnt++]=Edge(u,v);  
}

vector<int>del[maxn];

struct Plt{
    int x;ll c;
    Plt(int x=0,ll c=0):x(x),c(c){}
}pt[maxn];

struct Line{
    ll b;
    double k;
    Line(ll b,double k):b(b),k(k){} 
};

struct Q{
    int s,x,id;
    inline void input(int i){
        scanf("%d%d",&s,&x);id=i;
    }   
    inline bool operator < (const Q &rhs)const{
        return x<rhs.x; 
    }
}ask[maxn];

struct Node{
    vector<Line>st;
    Node *lc,*rc;int pos;   //pos表示上一次取到最优值的地方 
    Node(){st.clear();lc=rc=NULL;pos=0;}
    inline void push(Line x){st.push_back(x);}
    inline void pop(){st.pop_back();}
}*root;

#define mid ((l+r)>>1)
void build(Node* &p,int l,int r){
    p=new Node();if(l==r) return ;
    build(p->lc,l,mid);build(p->rc,mid+1,r);
}

inline double getx(Line a,Line b){
    return  (b.b-a.b)/(a.k-b.k);
}

inline void output(vector<Line>vc,int sz){
    for(int i=0;i<sz;++i){
        printf("[%d]:k=%.2f b=%lld",i,vc[i].k,vc[i].b); 
    }
    puts("\n");
}

void updata(Node* p,int l,int r,int L,int R,Line li)
{
    if(L<=l&&r<=R){
        int sz=p->st.size();

//      output(p->st,sz);

        while(sz>=2&&p->st[sz-1].k!=li.k&&getx(p->st[sz-2],p->st[sz-1]) > getx(p->st[sz-2],li)) --sz,p->pop();  
        if(!sz||p->st[sz-1].k!=li.k) p->push(li);
        return ;
    }
    if(L<=mid) updata(p->lc,l,mid,L,R,li);
    if(mid< R) updata(p->rc,mid+1,r,L,R,li);
}

const ll INF=1ll<<60;

inline ll gety(Line l,int x){return 1ll*x*x+l.k*x+l.b;}

ll query(Node* p,int l,int r,int pos,int x)
{
    ll ans=INF;int sz=p->st.size();
    while(p->pos+1 < sz && gety(p->st[p->pos],x) > gety(p->st[p->pos+1],x)) ++p->pos;
    if(sz) ans=gety(p->st[p->pos],x);
    if(l==r) return ans;
    if(pos<=mid) return min(ans,query(p->lc,l,mid,pos,x));
    else return min(ans,query(p->rc,mid+1,r,pos,x));
}

inline void init_data()
{
    cin>>n>>m>>c0;
    memset(first,-1,sizeof first);
    int op,fr,id,x,y,z; ll c;
    for(int i=1;i<n;++i)
    {
        tmp[i]=i;
        scanf("%d%d%d",&op,&fr,&id);
        add_edge(fr,i);
        if(op==0){
            add[id]=i;
            scanf("%d%d%d%lld",&x,&y,&z,&c);
            pt[id]=Plt(x,c);
        }
        else del[id].push_back(i);
    }

    for(int i=1;i<=m;++i) ask[i].input(i);
}

int Ln[maxn],Rn[maxn],dfs_clk;
void dfs(int u)
{
    Ln[u]=++dfs_clk;
    for(int i=first[u];i!=-1;i=Next[i]) 
        dfs(e[i].v);
    Rn[u]=dfs_clk;
}

inline ll getb(int x){return 1ll*pt[x].x*pt[x].x+pt[x].c;}

inline int cmp(const int &a,const int &b){
    return pt[a].x < pt[b].x||(pt[a].x==pt[b].x&&getb(a) < getb(b));
}
inline int cmp2(const int &a,const int &b){
    return Ln[a]<Ln[b];
}

ll ans[maxn];

inline void solve()
{
    dfs(0); build(root,1,n);

    sort(tmp+1,tmp+n,cmp);
    for(int i=1;i<n;++i)
    {
        int id=tmp[i];

        if(!add[id]) continue;
        Line l=Line(1ll*pt[id].x*pt[id].x+pt[id].c,-2*pt[id].x);
        if(!del[id].size()){
            updata(root,1,n,Ln[add[id]],Rn[add[id]],l);
        }
        else{
            sort(del[id].begin(),del[id].end(),cmp2);

//          for(int i=0;i<del[id].size();++i)
//              printf("%d ",del[id][i]);puts("");

            if(Ln[add[id]] < Ln[del[id][0]]) 
                updata(root,1,n,Ln[add[id]],Ln[del[id][0]]-1,l);
            if(Rn[del[id][del[id].size()-1]] < Rn[add[id]])
                updata(root,1,n,Rn[del[id][del[id].size()-1]]+1,Rn[add[id]],l);
            for(int i=0,sz=del[id].size();i<sz-1;++i)
                if(Rn[del[id][i]]+1 <= Ln[del[id][i+1]]-1)
                    updata(root,1,n,Rn[del[id][i]]+1,Ln[del[id][i+1]]-1,l);
        }
    }

    sort(ask+1,ask+m+1);
    for(int i=1;i<=m;++i)
        ans[ask[i].id]=min(1ll*ask[i].x*ask[i].x+c0,query(root,1,n,Ln[ask[i].s],ask[i].x));

    for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
}

int main()
{
    init_data();
    solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值