题面

线段树

题解

本题分两 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(nmlog⁡n)O(n\sqrt m\log n)O(nm​logn)。

#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】线段树(广义线段树,树上莫队)相关推荐

  1. YBTOJ洛谷P4074:糖果公园(树上莫队)

    文章目录 解析 update: 代码 所谓树上莫队,就是在树上的莫队 (逃) 传送门 解析 似乎就是树上的这道题 考虑如何转化为序列问题呢? 考虑dfs序 但是又一个问题... 似乎这条链的dfs序不 ...

  2. 【WC2013】糖果公园 树上莫队

    树上莫队,将树分块,以x,y为一二关键字,以时间为第三关键字.暴力修改. #include <iostream> #include <cstdio> #include < ...

  3. 【SPOJ】Count On A Tree II(树上莫队)

    [SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ...

  4. [学习笔记]树上莫队

    其实树上莫队是一个欧拉序而已嘛,像普通的莫队,特判一下出现过两次的值就行了 设 \(st_i\) 为 \(i\) 进栈的时间,\(ed_i\) 为 \(i\) 出栈的时间,\(dfn_x<dfn ...

  5. LG P4074 [WC2013] 糖果公园(带修莫队,树上莫队)

    LG P4074 [WC2013] 糖果公园 Solution 树上带修莫队,主要还是复习带修莫队和树上莫队. 带修莫队: 带修莫队要先对lll分块的序号作为第一关键字,对rrr分块的序号作为第二关键 ...

  6. 【莫队/树上莫队/回滚莫队】原理详解及例题:小B的询问(普通莫队),Count on a tree II(树上莫队),kangaroos(回滚莫队)

    文章目录 问题引入 介绍莫队算法及其实现过程 时间复杂度 莫队算法适用范围 莫队奇偶优化 普通莫队:小B的询问 树上莫队:SP10707 COT2 - Count on a tree II 回滚莫队: ...

  7. jzoj3360-[NOI2013模拟]苹果树【树上莫队,LCA】

    正题 题目大意 一棵树上每个节点有不同的颜色,然后每次询问(x,y,a,b)(x,y,a,b)(x,y,a,b)表示将颜色aaa看为颜色bbb的情况下询问xxx到yyy有多少不种的颜色. 解题思路 数 ...

  8. BZOJ.3052.[WC2013]糖果公园(树上莫队 带修改莫队)

    题目链接 BZOJ 当然哪都能交(都比在BZOJ交好),比如UOJ #58 //67376kb 27280ms //树上莫队+带修改莫队 模板题 #include <cmath> #inc ...

  9. 【SPOJ COT2】Count on a tree II,树上莫队

    Time:2016.09.07 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 第一次写树上莫队 被char哥怒裱一通 实际上还是比较简单的 序列的相关维护问题转移到树上一般都要涉及 ...

最新文章

  1. c语言界面怎么加图形,「分享」C语言如何编写图形界面
  2. C++中int与string的相互转换
  3. int?id与id??1 的意思
  4. 鸿蒙第一款手机,拿下“国内第一手机商”的OPPO,打算弃用华为鸿蒙?
  5. easypoi 导入失败返回错误文件_从Excel批量导入数据说到ForkJoin的原理
  6. 计算机视觉书籍学习记录——1最近点匹配
  7. 超级牛皮的oracle的分析函数over(Partition by...) 及开窗函数
  8. Windows无法启动 VMware Workstation server服务解决方法
  9. 单设施重心法选址matlab编程
  10. win7黑苹果双系统隐藏Clover多余启动项
  11. Linux GDB的实现原理
  12. word文档打破折号
  13. 使用selenium爬取唯品会
  14. thinkphp 打开速度缓慢,大多由于数据库读取问题!解决方法
  15. Adobe全家桶功能介绍
  16. 三维纽结与弦理论的一些猜想
  17. 绿色出行之路 中国越走越宽
  18. CC2640R2F BLE5.0 CC2640R2BLE5.0开发文档
  19. hitTest:(CGPoint)point withEvent:(UIEvent *)event
  20. 最纯洁的科研工作者——狄拉克

热门文章

  1. WINDOWS文件夹下的应用程序
  2. 《邱岳的产品实战》学习笔记:第9周
  3. 韩松EIE:Efficient Inference Engine on Compressed Deep Neural Network论文详解
  4. 西门子1200plc485轮询读写28个测试仪表,包括plc程序和触摸屏程序
  5. 解决IDEA 打不开问题
  6. 【偶遇小bug】浏览器无法翻译此网页解决
  7. (CCF202109-4)收集卡牌(概率DP)
  8. 神经网络处理表格数据,神经网络如何识别图像
  9. 2021版!万字UNIX网络编程学习笔记(套接字篇)
  10. 【计导非课系列】绪言——什么是“计导非课”系列?