题目

UOJ#198 [CTSC2016]时空旅行

题解

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



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

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

所以其实这是个一维上的问题。从一堆点中选一个出来。最小化(xi−x0)2+ci(x_i-x_0)^2+c_i,x0x_0是询问位置。

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

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

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

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

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

既然删除很麻烦,那题目中的删除怎么做呢?假如我在uu这个节点加了一个x星球,那么应该在[L[u],R[u]][L[u],R[u]]这个区间修改,如果我在vv 这个节点把xx删了(注意v一定是u的后代,而且除了u的节点的加操作不可能加的是x星球,这个是题意保证),那么我应该在[L[v],R[v]][L[v],R[v]]做删除操作,可是我们做不了删除操作233333,那咋办,我拆嘛,我拆成俩区间[L[u],L[v]−1][L[u],L[v]-1] 和[R[v]+1,R[u]][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;
}

UOJ#198 [CTSC2016]时空旅行相关推荐

  1. uoj 198: [CTSC2016]时空旅行

    一道比较套路的数据结构题.而且写起来也挺顺的. 首先可以发现y和z就是来卖萌的,无视即可.那么考虑一个点(星球)位置为x0,费用为c0,那么询问x到它的总花费为(x-x0)^2+c0=-2x0*x+( ...

  2. bzoj5077: [Ctsc2016]时空旅行【线段树+凸包】

    Description 2045年,人类的技术突飞猛进,已经找到了进行时空旅行的方法.小R得到了一台时空旅行仪,他想用它调查不同 时空中人类的发展状况.根据平行时空理论,宇宙中存在着很多独立的时空,每 ...

  3. [CTSC2016]时空旅行(斜率优化+线段树分治)

    洛谷题目传送门 解题思路 首先发现只有xxx和ccc是有用的 这些时空构成了一棵树,我们实际上要找一个点iii,满足对于给出的XXX min((X−xi)2+ci)min((X-x_i)^2+c_i) ...

  4. [CTSC2016]时空旅行

    一.题目 点此看题 二.解法 可以看出这些平行时空呈树形结构,每个星球都会出现在一段连续的dfndfndfn序中,而我们正是要对每个时空的星球中找最小值,那么可以把星球打在dfndfndfn的线段树上 ...

  5. [UOJ198][CTSC2016]时空旅行

    uoj description 你要维护若干个集合,每个集合都是有一个编号比他小的集合扩展而来,扩展内容为加入一个新的元素\((x,c)\)或者删除一个已有元素.集合的扩展关系之间构成一个树形结构. ...

  6. BZOJ5077: [Ctsc2016]时空旅行(线段树+凸包)

    传送门 题解: 首先答案为min(x−xi)2+cimin(x−xi)2+ci\min {(x-x_i)^2+c_i},移项得x2−2xix+x2i+cx2−2xix+xi2+cx^2 -2x_ix+ ...

  7. [CTSC2016]时空旅行 (线段树分治)

    前言 昨天学习了线段树分治,算法比较抽象,没有学得太具体,今天做一道例题练练手 --自闭前 题面上洛谷 题意 维护若干个集合,每个集合都是由一个编号比它小的集合加入一个二元组(x,c)(x,c)(x, ...

  8. CTSC2016时空旅行

    当时看这道题AC的人数比较多,就开了这道题. 很容易发现是这是一个有关凸包的题. 然后不知道怎么维护凸包,一直在想cdq,感觉复杂度不行,于是被这玩意难住了-- 幸好有亲学长yyh造福人类的题解:ht ...

  9. Luogu P5416 [CTSC2016]时空旅行

    题目 简化题意:你需要维护\(n\)个集合,集合元素为二元组\((x,v)\).集合\(i\)的产生方式是以某个原有集合\(p_i\)为样本,扩展或删除一个元素后得到新集合.有\(q\)次询问,每次给 ...

最新文章

  1. Python表白代码:“ 星光月夜烟花 皆归你,我也归你”
  2. C++ [](){} 匿名函数 lambda表达式
  3. 数据结构算法集---C++语言实现
  4. 78. Leetcode 264. 丑数 II (堆-技巧二-多路归并)
  5. 深度学习框架PyTorch一书的学习-第三章-Tensor和autograd-1-Tensor
  6. 百度 Java 后端三轮面试题,这些你会吗?
  7. 如何发送HTML表单数据
  8. sht20温湿度传感器原理图_温湿度传感器在孵化行业怎么应用
  9. 前后端分离 跨域问题解决
  10. CentOS 搭建Postfix+Dovecot简单邮件系统
  11. “”和“” java
  12. Zongsoft.Data 发布公告
  13. DELPHI串口通讯编程
  14. 从零搭建nginx服务器
  15. java分布式面试题_2021,Java最全的分布式面试题合集附答案,共2w字!
  16. CONTINUOUS CONTROL WITH DEEP REINFORCEMENT LEARNING
  17. 夕阳西下,天空燃烧着一片橘红色的晚霞
  18. 关于电子科技大学本科生宿舍热水情况调查
  19. 关于SAP 启用新公司时 选用的会计准则
  20. 【厚积薄发系列】C++项目总结21—VS远程调试技巧分享

热门文章

  1. Costco市值10年增长5倍的秘诀:水坝式经营
  2. html标签em和i的区别,HTML中strong与b,em与i标签的区别和使用建议
  3. iOS开发-集成阿里云实人认证
  4. 实例三十六:精确除法计算(*)
  5. [OHIF-Viewers]医疗数字阅片-医学影像-Module: Panel-自定义面板-中-es6-Object.defineProperty()定义属性...
  6. 数学建模之灰色关联分析
  7. bilibili 2021 技术对抗赛 算法与安全答题 答案
  8. 基于微信小程序的校园二手图书设计与开发
  9. 硬盘被写保护不可写Diskpart解决办法
  10. JSP页面调用log4j写日志文件