\(Solution\)

为了与普通区间区分,我们称线段树上某个结点 \([l, r]\) 为 块 \([l, r]\)

考虑模拟线段树区间查询 \([l, r]\) 时下放到的底部端点,必然是一堆连续的、不包含 \(l, r\) 区间的整块,和两个(或者是一整个)以 \(l, r\) 为左右端点的小散块(这里整块和散块没有大小性质区分,只是为了区分端点区间和内部区间)。

若一个块 \([x, y]\) 具有性质 \(N\),则其为所有左端点为 \(x\) 的块中的最大块

若一个块 \([x, y]\) 具有性质 \(M\),则其为所有右端点为 \(y\) 的块中的最大块

引理:除根结点外,其余所有点均恰好满足 \(N\) 性质或 \(M\) 性质的其中一条。

证明:考虑某个结点 \([l, r]\) 其必然至少满足一条性质,而其父节点必然包含了其某一端点,所以至多只满足一条,由此得到唯一性。

更进一步的,区间 \([l, r]\) 覆盖到的所有块,左边一块连续的必然满足性质 \(N\),右边一块必然满足性质 \(M\)(\([1, n]\) 比较特殊)。

那么显然有个转化就出来了

设 \(maxl_i, maxr_i\) 为以 \(i\) 为线段左端点的块的最大的右端点,和 以 \(i\) 为线段右端点的线段的最小的左端点。

构建两颗新树 \(L, R\),在 \(L\) 中连边 \((maxl_i+1, i)\),\((maxr_i-1, r)\) 边权分别是对应线段的标号。

考虑一个区间 \([a, b]\) 映射到树 \(L, R\) 上,设指针 \(u=a/b\) 从 \(L/R\) 树上往上跳,直到到根节点或父结点 \(u>b/u<a\) 为止(准确来说是跳到 \(maxl_{x}>b/maxr_{x}<a\) 的第一个点 \(x\)),那么这个区间映射到线段树的编号集合就是向上跳过程中经过的边权集合,注意到这些边在 \(L/R\) 树上必然各连成一条链(注意 \([1, n]\) 只需要取一边即可)。

处理询问,首先将 \([a, b]\) \([c, d]\) 各自映射,假设得到集合为 \(S^l_{[a, b]},S^r_{[a, b]},S^l_{[c, d]},S^r_{[c, d]}\),那么现在需要考虑的就是前二者对后二者贡献,将连差分成到根节点的路径计算。

现在问题就是,一堆询问,求两个路径之间点对距离的和,rush 成莫队,变成点到路径上点的距离和,点分树暴力算即可。

具体的,我们将树拍成括号序(dfs 进一次(左括号添加贡献),所有子树遍历完再进入一次(右括号删除贡献)),由于我们已经做了路径差分,所以实际上在括号序上表现为两个前缀的点的路径和,用 \(p_{L}, p_{R}\) 来表示询问的位置,那么我们可以将所有可能的询问 \((p_{L}, p_{R})\) 看成一种区间询问,当成莫队做。

当我们右移指针(不管是 \(p_{L}\) 还是 \(p_{R}\))时,相当于加入一个贡献点(可能是右括号),左移则相当于删除,这是与正常莫队不一样的点。

移动和删除计算贡献时我们直接对 \(B\) 树进行点分树计算即可,复杂度为 \(O(\log n)\)。

那么总复杂度为 \(O(n\sqrt{m}\log n)\)。

发现括号序版的树上莫队常数大得离谱。。。

Code:

#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#pragma GCC optimize(2) #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <cctype>
#include <vector>
#include <queue>
#include <cmath>
#include <bitset>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=0x3f3f3f3f;
const int cp=998244353;
inline int mod(int x){if(x>=cp) x-=cp;if(x<0) x+=cp;return x;}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
char ch=getchar();int x=0, f=1;
while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x){
if(x<0) putchar('-'), x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
int ret=1;
for(; b; b>>=1, a=1ll*a*a%cp)
if(b&1) ret=1ll*ret*a%cp;
return ret;
}
const int N=1e4+5;
const int S=N<<1;
const int M=1e5+5;
namespace Btree{
vi G[S];int ST[20][S], dfn[S], dep[S], fa[S];
int MX(int x, int y){return dep[x]<dep[y]?x:y;}
int LCA(int x, int y){
if(x==y) return x;x=dfn[x], y=dfn[y];if(x>y) swap(x, y);
int k=__lg(y-x);return fa[MX(ST[k][x+1], ST[k][y-(1<<k)+1])];
}
int dist(int x, int y){if(!x||!y) return 0;return dep[x]+dep[y]-2*dep[LCA(x, y)];}
void dfs(int x){ST[0][dfn[x]=++dfn[0]]=x;for(auto v:G[x]) if(!dep[v]) dep[v]=dep[x]+1, fa[v]=x, dfs(v);}
int tim, siz[S], vis[S], dfa[S], Mn, g;
void findrt(int x, int all){
int mx=0;siz[x]=1, vis[x]=tim;
for(auto v:G[x]) if(vis[v]<tim) findrt(v, all), siz[x]+=siz[v], mx=max(mx, siz[v]);
mx=max(mx, all-siz[x]);if(mx<Mn) Mn=mx, g=x;
}
int mxd;
void dfz(int rt, int dep=0){
mxd=max(mxd, dep);++tim;findrt(rt, 114514);++tim;vis[rt]=INF;for(auto v:G[rt])
if(vis[v]<tim) Mn=siz[v], g=v, findrt(v, siz[v]), dfa[g]=rt, dfz(g, dep+1);
}
void init(int nd){
for(int i=1, u, v; i<nd; ++i)
u=read(), v=read(), G[u].pb(v), G[v].pb(u);
dfs(dep[1]=1);
for(int i=1; i<=__lg(nd); ++i)
for(int x=1; x+(1<<i)-1<=nd; ++x)
ST[i][x]=MX(ST[i-1][x], ST[i-1][x+(1<<i-1)]);
dfz(1);//printf("!!!!%d\n", mxd);
// for(int i=1; i<=nd; ++i) printf("%d ", dfa[i]);puts("");
}
}
int n, m, nd;
struct Tree{
int rt, fa[N][20], w[N], bs[S], top, pos[N];vi G[N];
void dfs(int x){
for(int i=1; i<20; ++i) fa[x][i]=fa[fa[x][i-1]][i-1];
if(x^rt) bs[pos[x]=++top]=w[x];//, printf("%d ", w[x], x);
for(auto v:G[x]) dfs(v);if(x^rt) bs[++top]=-w[x];//, printf("%d ", -w[x], x);
}
void init(){for(int i=1; i<=n; ++i) G[fa[i][0]].pb(i);fa[rt][0]=rt;dfs(rt);}
}L, R;
void find(int a, int b, int &u, int &v){
u=a, v=b;//if(a==1&&b==n) return (void)(u=n+1);//[1, n] 会重复,只用其中一个就行了(但是数据好像造假了啊)
for(int i=19; i>=0; --i){
if(L.fa[u][i]<=b) u=L.fa[u][i];
if(R.fa[v][i]>=a) v=R.fa[v][i];
}
if(L.fa[u][0]-1==b) u=L.fa[u][0];
if(R.fa[v][0]+1==a) v=R.fa[v][0];
}
int p;//2n-1
void build(int k, int l, int r){
if(l<r){
int mid=read();
build(++p, l, mid);build(++p, mid+1, r);
}
L.fa[l][0]=r+1, L.w[l]=k, R.fa[r][0]=l-1, R.w[r]=k;
}
ll ans[M];int B;
struct ask{int l, r, qid, op;};
bool cmp(const ask a, const ask b){
if(a.l/B==b.l/B) return ((a.l/B)&1)?a.r<b.r:a.r>b.r;
else return a.l<b.l;
}
vector <ask> Q[3];//00-LL 01-LR 11 RR
void add(int l, int opl, int r, int opr, int id, int op){
if(!opr) swap(l, r), swap(opl, opr);/*10 变成 01*/int opx=opl+opr;
l=opx^2?L.pos[l]:R.pos[l], r=opx^0?R.pos[r]:L.pos[r];
if(l&&r) Q[opx].pb((ask){l, r, id, op});//如果是根的话就不需要算了}
}
ll res;struct node{ll sum, sumf;int x, cnt;}f[S][2];
void upd(int sid, int p, int op){
int xp=(p>0?1:-1), x=p<0?-p:p;
for(int u=x, lst=0; u; ){
int disu=Btree :: dist(u, x);
res+=1ll*op*xp*((f[u][sid^1].sum-f[lst][sid^1].sumf)+
1ll*disu*(f[u][sid^1].cnt-f[lst][sid^1].cnt));
node *fu=&f[u][sid];fu->cnt+=op*xp, fu->sum+=op*xp*disu;
lst=u, u=Btree :: dfa[u];fu->sumf+=op*xp*Btree :: dist(u, x);
}
}
void MoA(int tp){//莫队
res=0;for(int i=1; i<=nd; ++i) f[i][0]=f[i][1]=(node){0, 0, i, 0};
int q=Q[tp].size();if(!q) return ;B=2*nd/sqrt(3*q)+25;
sort(Q[tp].begin(), Q[tp].end(), cmp);
int l=0, r=0;
for(auto me:Q[tp]){
int x;
while(l<me.l) ++l, x=tp^2?L.bs[l]:R.bs[l], upd(0, x, 1);
while(l>me.l) x=tp^2?L.bs[l]:R.bs[l], upd(0, x, -1), --l;
while(r<me.r) ++r, x=tp^0?R.bs[r]:L.bs[r], upd(1, x, 1);
while(r>me.r) x=tp^0?R.bs[r]:L.bs[r], upd(1, x, -1), --r;
ans[me.qid]+=me.op*res;
}
}
signed main(){
// freopen("hack.in", "r", stdin);
// freopen("hack.out", "w", stdout);
n=read(), m=read(), nd=2*n-1;L.rt=n+1, R.rt=0;build(p=1, 1, n);
L.init(), R.init();Btree :: init(nd);
for(int i=1; i<=m; ++i){
int a=read(), b=read(), u, v;find(a, b, u, v);//a-u b-v
int c=read(), d=read(), x, y;find(c, d, x, y);//c-x d-y
if(a>b||c>d) exit(0);
add(a, 0, c, 0, i, 1);add(u, 0, c, 0, i, -1);
add(u, 0, x, 0, i, 1);add(a, 0, x, 0, i, -1);
add(a, 0, d, 1, i, 1);add(u, 0, d, 1, i, -1);
add(u, 0, y, 1, i, 1);add(a, 0, y, 1, i, -1);
add(b, 1, c, 0, i, 1);add(v, 1, c, 0, i, -1);
add(v, 1, x, 0, i, 1);add(b, 1, x, 0, i, -1);
add(b, 1, d, 1, i, 1);add(v, 1, d, 1, i, -1);
add(v, 1, y, 1, i, 1);add(b, 1, y, 1, i, -1);
//16 个询问,堂堂完结!
}
MoA(1), MoA(0), MoA(2);
for(int i=1; i<=m; ++i) printf("%lld\n", ans[i]);
return 0;
}

XSY3490 / ZROI P618 广义线段树 + 莫队 + 点分树的更多相关文章

  1. 洛谷P3567 KUR-Couriers [POI2014] 主席树/莫队

    正解:主席树/莫队 解题报告: 传送门! 这题好像就是个主席树板子题的样子,,,? 毕竟,主席树的最基本的功能就是,维护一段区间内某个数字的个数 但是毕竟是刚get到主席树,然后之前做的一直是第k大, ...

  2. SPOJ DQUERY - D-query (莫队算法|主席树|离线树状数组)

    DQUERY - D-query Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query ...

  3. 【CodeForces】700 D. Huffman Coding on Segment 哈夫曼树+莫队+分块

    [题目]D. Huffman Coding on Segment [题意]给定n个数字,m次询问区间[l,r]的数字的哈夫曼编码总长.1<=n,m,ai<=10^5. [算法]哈夫曼树+莫 ...

  4. 【BZOJ3207】花神的嘲讽计划I 可持久化线段树/莫队

    看到题目就可以想到hash 然后很自然的联想到可持久化权值线段树 WA:base取了偶数 这道题还可以用莫队做,比线段树快一些 可持久化线段树: #include<bits/stdc++.h&g ...

  5. [BZOJ3236][AHOI2013]作业:树套树/莫队+分块

    分析 第一问随便搞,直接说第二问. 令原数列为\(seq\),\(pre_i\)为\(seq_i\)这个值上一个出现的位置,于是可以简化询问条件为: \(l \leq i \leq r\) \(a \ ...

  6. 【序列莫队+二分答案+树状数组】POJ2104-K-th Number

    [题目大意] 给出一个长度为n的序列和m组查询(i,j,k),输出[i,j]中的第k大数. [思路] 先离散化然后莫队分块.用树状数组来维护当前每个值的个数,然后对于每次询问二分答案即可. 又一次实力 ...

  7. CodeForces - 220B Little Elephant and Array (莫队+离散化 / 离线树状数组)

    题意:N个数,M个查询,求[Li,Ri]区间内出现次数等于其数值大小的数的个数. 分析:用莫队处理离线问题是一种解决方案.但ai的范围可达到1e9,所以需要离散化预处理.每次区间向外扩的更新的过程中, ...

  8. HDU5589:Tree(莫队+01字典树)

    传送门 题意 略 分析 f[u]表示u到根的边的异或 树上两点之间的异或值为f[u]^f[v], 然后将查询用莫队算法分块,每个点插入到字典树中,利用字典树维护两点异或值大于等于M复杂度O(N^(3/ ...

  9. CodeForces - 375D Tree and Queries (莫队+dfs序+树状数组)

    You have a rooted tree consisting of n vertices. Each vertex of the tree has some color. We will ass ...

  10. P3180-[HAOI2016]地图【圆方树,莫队,分块】

    正题 题目链接:https://www.luogu.com.cn/problem/P3180 题目大意 \(n\)个点\(m\)条边的一个仙人掌,有点权. \(Q\)次询问给出\(op,x,y\),封 ...

随机推荐

  1. sqlite的firedac显示设置

  2. FastAPI中Pydantic异步分布式唯一性校验

    title: FastAPI中Pydantic异步分布式唯一性校验 date: 2025/04/02 00:47:55 updated: 2025/04/02 00:47:55 author: cmd ...

  3. 强化学习(on-policy)同步并行采样(on-line)的并行化效率分析

    在强化学习中(on-line)的算法如果是on-policy的算法都是需要较大的采样样本的,因此采样的效率往往对整个算法运行效率有着自关重要的影响,在deepmind(Google)公司的强化学习的并 ...

  4. 【Linux】编译用于exynos4412(ARM)的Linux-3.14内核

    [Linux]编译用于exynos4412(ARM)的Linux-3.14内核 零.准备 1.下载 Linux-3.14内核源代码 下载页面:https://www.kernel.org/pub/li ...

  5. .NET 平台上的开源模型训练与推理进展

    .NET 平台上的开源模型训练与推理进展 作者:痴者工良 博客:https://www.whuanle.cn 电子书仓库:https://github.com/whuanle/cs_pytorch M ...

  6. AspNetCore 请求产生 FreeSql 所有操作 SQL 日志输出到前端

    第一步:定义注入类型 public class CurdAfterLog : IDisposable { public static AsyncLocal<CurdAfterLog> Cu ...

  7. Nacos简介—2.Nacos的原理简介

    大纲 1.Nacos集群模式的数据写入存储与读取问题 2.基于Distro协议在启动后的运行规则 3.基于Distro协议在处理服务实例注册时的写路由 4.由于写路由造成的数据分片以及随机读问题 5. ...

  8. 毒瘤idea合集

    给定 \(n,m\) ,求: \[\sum_{i=1}^{n}\sum_{i=1}^{m}max\big(gcd(i,j)^i,lcm(i,j)^j\big) \]

  9. 信息资源管理综合题之“某国企投资IT应用人员减少但生成率没有实质性变化的IT黑洞问题”

    一.某大型国企在IT应用上投资了2000万美元,虽然蓝领工人数量大幅减少,但实际生产率并未有实质性变化 1.企业在IT应用上的巨额投资并未达到预期目标的这种现象被称为什么? 2.产生这现象的原因有哪些 ...

  10. 操作系统综合题之“短进程优先调度算法(Shortest-Process-First,SPF)和非抢占式优先权调度算法(Priority-Scheduling Lgorithm)的进程执行顺序并计算周转时间以及平均周转时间【分开计算题】”

    一.问题: 1.当系统采用短进程优先调度算法时,试写出进程的执行顺序,并计算各个进程的周转时间以及平均周转时间 2.当系统采用优先权调度算法时,试写出进程的执行顺序,并计算各个进程的周转时间以及平均周 ...