【XSY3490】线段树(广义线段树,树上莫队)
题面
线段树
题解
本题分两 Part 走。
Part 1
我们需要解决: 如何在广义线段树上快速区间定位节点。
对于有 nnn 个叶子节点、共 2n−12n-12n−1 个节点的广义线段树 AAA,我们定义 maxrlmaxr_lmaxrl 表示 AAA 中所有以 lll 为左端点的区间的右端点的最大值,minlrminl_rminlr 为 AAA 中所有以 rrr 为右端点的区间的左端点的最小值。然后再定义 LLL 树和 RRR 树,LLL 树中点 lll 对应 AAA 树中的点 [l,maxrl][l,maxr_l][l,maxrl],lll 的父亲为 maxrl+1maxr_l+1maxrl+1。RRR 树中 rrr 对应 AAA 树中的点 [minlr,r][minl_r,r][minlr,r],rrr 的父亲为 minlr−1minl_r-1minlr−1。
区间定位 [l,r][l,r][l,r] 的时候,我们在 LLL 树上从 lll 开始往上跳直到当前点 uuu 的 maxrumaxr_umaxru 恰好大于 rrr 为止,RRR 树上从 rrr 开始往上跳直到当前点 uuu 的 minluminl_uminlu 刚好小于 lll 为止,这两段所对应 AAA 树的节点就是我们区间定位得到的节点。
考虑这样做为什么是对的,我们称 AAA 树上一个节点为 “右儿子节点” 当且仅当它是其父亲的右儿子,“左儿子节点” 的定义同理。假设在 AAA 树上区间定位 [L,R][L,R][L,R] 所得到的的节点为 a1,⋯,ama_1,\cdots,a_ma1,⋯,am,那么肯定存在一个 kkk,使得 a1,⋯,aka_1,\cdots,a_ka1,⋯,ak 都是右儿子节点,ak+1,⋯,ama_{k+1},\cdots,a_mak+1,⋯,am 都是左儿子节点。
假设现在有一个 AAA 树上的节点,它的左端点 lll 已知,而且已知它是右儿子节点,那么我们能确定这个点吗?当然能,因为以某个 lll 为左端点的右儿子节点是唯一的,就是 [l,maxrl][l,maxr_l][l,maxrl]。
再回到区间定位,我们已经知道了 a1a_1a1 的左端点一定是 LLL,那么若 a1a_1a1 是右儿子节点,a1a_1a1 是被唯一确定的,我们先找到它,设为 ppp。若它超出 [L,R][L,R][L,R] 范围则 a1a_1a1 不可能是区间定位中的点,之后也不可能出现右儿子节点了,直接 break。否则,发现若 ppp 所对应的区间在 [L,R][L,R][L,R] 内,则它一定是 [L,R][L,R][L,R] 区间定位中的节点(即它一定是 a1a_1a1),因为其祖先所对应的区间一定会超出 [L,R][L,R][L,R]。然后也就知道了 a2a_2a2 的左端点,于是递归处理,直到当前找到的节点超出 [L,R][L,R][L,R] 范围为止。这样我们就能找到 a1,⋯,aka_1,\cdots,a_ka1,⋯,ak。这个过程就是在 LLL 树上不断跳祖先的过程。
同样地,我们也能用类似的方法找到 ak+1,⋯,ama_{k+1},\cdots,a_mak+1,⋯,am。这个过程就是在 RRR 树上不断跳祖先的过程。
至此,我们就把广义线段树上一段区间定位得到的节点转化为 LLL 树和 RRR 树上的两条祖先-后代链上对应的节点。
Part 2
现在是要求 L/RL/RL/R 树上一条链在 BBB 树中对应的点,到 L/RL/RL/R 树上一条链在 BBB 树中对应的点,的两两距离和。这里以 LLL 树到 RRR 树为例。
有一个单次询问复杂度关于点数成线性的虚树做法,但显然过不了。
做法是利用链的性质:差分之后转化为 LLL 树上一条到根的链和 RRR 树上一条到根的链在 BBB 树中对应的点的两两距离和,然后直接树上莫队。
树上莫队的具体做法是:两个指针分别在 LLL 树和 RRR 树的欧拉序上移动。当加入/删除一个点时,我们要求出它到另一棵树上一条到根的链在 BBB 树中对应的点的距离和,使用点分治即可。
时间复杂度 O(nmlogn)O(n\sqrt m\log n)O(nmlogn)。
#include<bits/stdc++.h>#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
#define INF 0x7fffffff
#define ll long long
#define LN 14
#define N 10010
#define M 100010using namespace std;inline int read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^'0');ch=getchar();}return x*f;
}int n,m;namespace Btree
{const int V=N<<1;int nn;int cnt,head[V],to[V<<1],nxt[V<<1];int ns,maxn,rt,fa[V],size[V];bool vis[V];vector<int>Dis[V][2];void adde(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}void getsize(int u,int fa){size[u]=1;for(int i=head[u];i;i=nxt[i]){int v=to[i];if(vis[v]||v==fa) continue;getsize(v,u);size[u]+=size[v];}}void getroot(int u,int fa){int nmax=0;for(int i=head[u];i;i=nxt[i]){int v=to[i];if(vis[v]||v==fa) continue;getroot(v,u);nmax=max(nmax,size[v]);}nmax=max(nmax,ns-size[u]);if(nmax<maxn) maxn=nmax,rt=u;}void getdis(int u,int fa,int dis,bool opt){Dis[u][opt].push_back(dis);for(int i=head[u];i;i=nxt[i]){int v=to[i];if(vis[v]||v==fa) continue;getdis(v,u,dis+1,opt);}}void solve(int u){vis[u]=1;getdis(u,0,0,0);for(int i=head[u];i;i=nxt[i]){int v=to[i];if(vis[v]) continue;getsize(v,0);ns=size[v],maxn=INF,getroot(v,0);getdis(v,0,1,1);fa[rt]=u,solve(rt);}}void init(){nn=(n<<1)-1;for(int i=1;i<nn;i++){int u=read(),v=read();adde(u,v),adde(v,u);}getsize(1,0);ns=size[1],maxn=INF,getroot(1,0);solve(rt);}struct Dtree{int sdis[V][2],snum[V][2];void insert(int u,int coef){int now=u;for(int i=(int)Dis[u][0].size()-1;i>=0;i--){sdis[now][0]+=coef*Dis[u][0][i],snum[now][0]+=coef;if(i) sdis[now][1]+=coef*Dis[u][1][i-1],snum[now][1]+=coef;now=fa[now];}}int query(int u){int now=u,ans=0;for(int i=(int)Dis[u][0].size()-1;i>=0;i--){ans+=sdis[now][0]+snum[now][0]*Dis[u][0][i];if(i) ans-=sdis[now][1]+snum[now][1]*Dis[u][1][i-1];now=fa[now];}return ans;}}t1,t2;
}namespace Atree
{struct Tree{int rt;int cnt,head[N],pid[N],nxt[N<<1],to[N<<1]; int fa[N][LN],d[N];int idx,in[N],out[N];pii p[N<<1];void adde(int u,int v,int pp){pid[v]=pp;to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}void dfs(int u){if(u!=rt) p[in[u]=++idx]=mk(pid[u],1);for(int i=1;i<=13&&fa[u][i-1]!=-1;i++)fa[u][i]=fa[fa[u][i-1]][i-1];for(int i=head[u];i;i=nxt[i]){int v=to[i];fa[v][0]=u,d[v]=d[u]+1;dfs(v);}if(u!=rt) p[out[u]=++idx]=mk(pid[u],-1);}void init(){memset(fa,-1,sizeof(fa));d[rt]=1,dfs(rt);}}L,R;int node;pii maxr[N],minl[N];void dfs(int l,int r){int u=++node;if(l!=r){int mid=read();dfs(l,mid),dfs(mid+1,r);}maxr[l]=mk(r,u),minl[r]=mk(l,u);}void init(){dfs(1,n);for(int i=1;i<=n;i++){L.adde(maxr[i].fi+1,i,maxr[i].se);R.adde(minl[i].fi-1,i,minl[i].se);}L.rt=n+1,R.rt=0,L.init(),R.init();}void find(int l,int r,pii &Lp,pii &Rp){if(maxr[l].fi>r) Lp=mk(1,0);else{int a=l;for(int i=13;i>=0;i--)if(L.fa[a][i]!=-1&&L.fa[a][i]!=L.rt&&maxr[L.fa[a][i]].fi<=r)a=L.fa[a][i];Lp=mk(L.in[a],L.in[l]);}if(minl[r].fi<l||(l==1&&r==n)) Rp=mk(1,0);else{int a=r;for(int i=13;i>=0;i--)if(R.fa[a][i]!=-1&&R.fa[a][i]!=R.rt&&minl[R.fa[a][i]].fi>=l)a=R.fa[a][i];Rp=mk(R.in[a],R.in[r]);}}
}namespace Solve
{using Btree::Dtree;using Btree::t1;using Btree::t2;ll sum,ans[M];int len,block[N<<1];struct data{int p1,p2,id,coef;data(){};data(int _p1,int _p2,int _id,int _opt){p1=_p1,p2=_p2,id=_id,coef=_opt;}};vector<data>qll,qlr,qrr;void insq(vector<data> &q,pii p1,pii p2,int id){q.push_back(data(p1.se,p2.se,id,1));q.push_back(data(p1.se,p2.fi-1,id,-1));q.push_back(data(p1.fi-1,p2.se,id,-1));q.push_back(data(p1.fi-1,p2.fi-1,id,1));}void upd(int u,Dtree &t1,Dtree &t2,int coef){sum+=coef*t2.query(u);t1.insert(u,coef);}void solve(vector<data> &q,pii *id1,pii *id2){sort(q.begin(),q.end(),[&](data a,data b){if(block[a.p1]!=block[b.p1]) return block[a.p1]<block[b.p1];return a.p2<b.p2;});int tmp1=0,tmp2=0;for(data now:q){while(tmp1<now.p1) ++tmp1,upd(id1[tmp1].fi,t1,t2,id1[tmp1].se);while(tmp1>now.p1) upd(id1[tmp1].fi,t1,t2,-id1[tmp1].se),tmp1--;while(tmp2<now.p2) ++tmp2,upd(id2[tmp2].fi,t2,t1,id2[tmp2].se);while(tmp2>now.p2) upd(id2[tmp2].fi,t2,t1,-id2[tmp2].se),tmp2--;ans[now.id]+=now.coef*sum;}while(tmp1) upd(id1[tmp1].fi,t1,t2,-id1[tmp1].se),tmp1--;while(tmp2) upd(id2[tmp2].fi,t2,t1,-id2[tmp2].se),tmp2--;}void main(){len=125;for(int i=1,t=n<<1;i<=t;i++)block[i]=(i-1)/len+1;for(int i=1;i<=m;i++){int a=read(),b=read(),c=read(),d=read();pii al,ar,bl,br;Atree::find(a,b,al,ar);Atree::find(c,d,bl,br);insq(qll,al,bl,i),insq(qlr,al,br,i),insq(qlr,bl,ar,i),insq(qrr,ar,br,i);}solve(qll,Atree::L.p,Atree::L.p);solve(qlr,Atree::L.p,Atree::R.p);solve(qrr,Atree::R.p,Atree::R.p);for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);}
}int main()
{// freopen("ex_segment3.in","r",stdin);
// freopen("ex_segment3_my.out","w",stdout);n=read(),m=read();Atree::init();Btree::init();Solve::main();return 0;
}
【XSY3490】线段树(广义线段树,树上莫队)相关推荐
- YBTOJ洛谷P4074:糖果公园(树上莫队)
文章目录 解析 update: 代码 所谓树上莫队,就是在树上的莫队 (逃) 传送门 解析 似乎就是树上的这道题 考虑如何转化为序列问题呢? 考虑dfs序 但是又一个问题... 似乎这条链的dfs序不 ...
- 【WC2013】糖果公园 树上莫队
树上莫队,将树分块,以x,y为一二关键字,以时间为第三关键字.暴力修改. #include <iostream> #include <cstdio> #include < ...
- 【SPOJ】Count On A Tree II(树上莫队)
[SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ...
- [学习笔记]树上莫队
其实树上莫队是一个欧拉序而已嘛,像普通的莫队,特判一下出现过两次的值就行了 设 \(st_i\) 为 \(i\) 进栈的时间,\(ed_i\) 为 \(i\) 出栈的时间,\(dfn_x<dfn ...
- LG P4074 [WC2013] 糖果公园(带修莫队,树上莫队)
LG P4074 [WC2013] 糖果公园 Solution 树上带修莫队,主要还是复习带修莫队和树上莫队. 带修莫队: 带修莫队要先对lll分块的序号作为第一关键字,对rrr分块的序号作为第二关键 ...
- 【莫队/树上莫队/回滚莫队】原理详解及例题:小B的询问(普通莫队),Count on a tree II(树上莫队),kangaroos(回滚莫队)
文章目录 问题引入 介绍莫队算法及其实现过程 时间复杂度 莫队算法适用范围 莫队奇偶优化 普通莫队:小B的询问 树上莫队:SP10707 COT2 - Count on a tree II 回滚莫队: ...
- jzoj3360-[NOI2013模拟]苹果树【树上莫队,LCA】
正题 题目大意 一棵树上每个节点有不同的颜色,然后每次询问(x,y,a,b)(x,y,a,b)(x,y,a,b)表示将颜色aaa看为颜色bbb的情况下询问xxx到yyy有多少不种的颜色. 解题思路 数 ...
- BZOJ.3052.[WC2013]糖果公园(树上莫队 带修改莫队)
题目链接 BZOJ 当然哪都能交(都比在BZOJ交好),比如UOJ #58 //67376kb 27280ms //树上莫队+带修改莫队 模板题 #include <cmath> #inc ...
- 【SPOJ COT2】Count on a tree II,树上莫队
Time:2016.09.07 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 第一次写树上莫队 被char哥怒裱一通 实际上还是比较简单的 序列的相关维护问题转移到树上一般都要涉及 ...
最新文章
- c语言界面怎么加图形,「分享」C语言如何编写图形界面
- C++中int与string的相互转换
- int?id与id??1 的意思
- 鸿蒙第一款手机,拿下“国内第一手机商”的OPPO,打算弃用华为鸿蒙?
- easypoi 导入失败返回错误文件_从Excel批量导入数据说到ForkJoin的原理
- 计算机视觉书籍学习记录——1最近点匹配
- 超级牛皮的oracle的分析函数over(Partition by...) 及开窗函数
- Windows无法启动 VMware Workstation server服务解决方法
- 单设施重心法选址matlab编程
- win7黑苹果双系统隐藏Clover多余启动项
- Linux GDB的实现原理
- word文档打破折号
- 使用selenium爬取唯品会
- thinkphp 打开速度缓慢,大多由于数据库读取问题!解决方法
- Adobe全家桶功能介绍
- 三维纽结与弦理论的一些猜想
- 绿色出行之路 中国越走越宽
- CC2640R2F BLE5.0 CC2640R2BLE5.0开发文档
- hitTest:(CGPoint)point withEvent:(UIEvent *)event
- 最纯洁的科研工作者——狄拉克
热门文章
- WINDOWS文件夹下的应用程序
- 《邱岳的产品实战》学习笔记:第9周
- 韩松EIE:Efficient Inference Engine on Compressed Deep Neural Network论文详解
- 西门子1200plc485轮询读写28个测试仪表,包括plc程序和触摸屏程序
- 解决IDEA 打不开问题
- 【偶遇小bug】浏览器无法翻译此网页解决
- (CCF202109-4)收集卡牌(概率DP)
- 神经网络处理表格数据,神经网络如何识别图像
- 2021版!万字UNIX网络编程学习笔记(套接字篇)
- 【计导非课系列】绪言——什么是“计导非课”系列?