重修 分块和莫队
带修莫队
相信我会了,这里只考虑时间复杂度。
设我们块大小(\(l,r\) 两轴块大小)为 \(B\),则时间复杂度为 \(O\) 里面:
上柿为三维长方体 \(x\in[1,n],y\in[1,n],z\in[1,m]\) 内 \(m\) 个点以 \(x,y\) 轴分别以 \(B\) 块长分块后用一根每一段与某坐标轴平行的折线串起来,线以分块策略下的最劣情况长度。
(由于我不会画 3D 的图,所以自行脑补 qwq)
我们若规定 \(m>>B\),则上述柿子变为
均衡一下得到 \(B=n^{2/3}\),总复杂度为 \(n^{2/3}m\)。
为啥别的题解得到的 \(B\) 和我的不一样捏。
回滚莫队
分成两种:只增加、只删除。
只删除的例题:P8078 [WC2022] 秃子酋长
二次离线莫队
设莫队单次修改的时间为 \(t\),则二次离线莫队将莫队从 \(O(nt\sqrt{n})\) 优化到 \(O(nt+n\sqrt{n})\),当然前提是满足区间可减性。
给你一个序列 \(a\),每次查询给一个区间 \([l,r]\),查询 \(l \leq i< j \leq r\),且 \(popcnt(a_i \oplus a_j)=k\) 的二元组 \((i,j)\) 的个数.\(\oplus\) 是指按位异或。\(n,q\le 10^5,0\le a_i,k<2^{14}\)。
设 \(m\) 为 \([0,V)\) 中 \(popcnt=k\) 的个数,我们预处理这些数,最后时间复杂度也和 \(m\) 有关。
设 \(f(x,l,r)\) 表示
的 \(y\) 的个数。
由于区间可减,得到
所以下文用 \(f(x,y)\) 来简写 \(f(x,1,y)\)。
只有当 \(k=0\) 时 \(popcnt(a_x\oplus a_x)=k\),所以得到
我们先正常莫队(甚至可以奇偶优化)。举个例子(莫队区间设为 \([L,R]\))。
当 \(L\) 增大至 \(l\) 时,此询问比上次减少
\(L\) 增大、\(R\) 减小、\(R\) 增大 同理,不再赘述。
我们可以用桶 \(O(nm)\) 预处理出 \(p(x)\)。所以剩下的只要拎出 \(\sum_{x=L}^{l-1} f(x,R)\) 类的东西求即可,最后再代回来算答案。
接下来就是二次离线部分了。
我们将每一个形如 \(\sum_{x=l}^{r} f(x,i)\) 的询问挂在 \(i\) 位置上。
然后再用桶扫,同时计算每个位置上的询问。
这部分是 \(O(nm+n\sqrt{n})\),\(n\sqrt{n}\) 是因为二级询问有 \(O(n\sqrt{n})\) 个,每个只要 \(O(1)\) 回答。
然后就做完了,总时间 \(O(nm+n\sqrt{n})\),与开头说的相同。
例题代码:
点击查看代码
//Said no more counting dollars. We'll be counting stars.
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define mem(x,y) memset(x,y,sizeof(x))
#define For(i,j,k) for(int i=j;i<=k;i++)
#define Rof(i,j,k) for(int i=j;i>=k;i--)
#define int long long
#define N 100010
#define V 16384//值域
vector<int> bu;//0~V-1 popcnt=k 的数
int n,m,k,a[N],b[N],gap;
struct Que{
int l,r,id,ans;//ans:比莫队中上次询问答案的增量
friend bool operator<(Que x,Que y){return b[x.l]==b[y.l]?((b[x.l]&1)?x.r<y.r:x.r>y.r):x.l<y.l;}
}q[N];
int p[N];//ct((i+1)->[1,i]) ct表示贡献
int t[V];//桶
int ans[N];//最终答案
vector<tuple<int,int,int,int> > v[N];
//第一次莫队留下的询问v[i].<l,r,id,val>:[l,r] 区间中所有 x,sum ct(x->[1,i]),乘系数 val 贡献给询问 i
signed main(){
scanf("%lld%lld%lld",&n,&m,&k);
For(i,0,V-1) if(__builtin_popcount(i)==k) bu.pb(i);
For(i,1,n) scanf("%lld",a+i);
For(i,1,m) scanf("%lld%lld",&q[i].l,&q[i].r),q[i].id=i;
gap=sqrt(n);
For(i,1,n) b[i]=(i-1)/gap+1;
sort(q+1,q+1+m);
For(i,1,n){
for(int j:bu) t[a[i]^j]++;
p[i]=t[a[i+1]];
}
int L=1,R=0,l,r;
For(i,1,m){
l=q[i].l,r=q[i].r;
if(L<l) v[R].pb(L,l-1,i,-1);
while(L<l){q[i].ans+=p[L-1]+(!k);++L;}
if(R>r) v[L-1].pb(r+1,R,i,1);
while(R>r){q[i].ans-=p[R-1]; --R;}
if(L>l) v[R].pb(l,L-1,i,1);
while(L>l){q[i].ans-=p[L-2]+(!k);--L;}
if(R<r) v[L-1].pb(R+1,r,i,-1);
while(R<r){q[i].ans+=p[R]; ++R;}
}
mem(t,0);
int id,val,tmp;
For(i,1,n){
for(int j:bu) t[a[i]^j]++;
for(auto x:v[i]){
tie(l,r,id,val)=x;
For(j,l,r){
tmp=t[a[j]];
q[id].ans+=tmp*val;
}
}
}
For(i,1,m) q[i].ans+=q[i-1].ans;//累计
For(i,1,m) ans[q[i].id]=q[i].ans;//重排
For(i,1,m) printf("%lld\n",ans[i]);
return 0;}
树上莫队(fake)
指的是平摊成欧拉序后在序列上做莫队。
树上莫队(real)
首先先得会树分块。
为了保证最终莫队复杂度的正确性,我们需要做到:
-
属于同一块的节点之间的距离不大。
-
每个块中的节点不能太多也不能太少。
-
每个节点都要属于一个块。
-
编号相邻的块之间的距离不能太大。
我们让树分块后依次顺序编号就正好满足了第四个条件了。
分块后的排序方法:若路径 \((u,v)\) 的 \(u\) 的时间戳大于 \(v\) 那么交换 \(u,v\)。然后按照 \(u\) 所在块为第一关键字,\(v\) 的时间戳为第二关键字排序。
注意这里有一个大坑点:
在指针移动的过程中,我们肯定是让移动前的位置和移动后的位置一起向 lca 靠近。然后利用 \(vis\) 标记来
判断这个点是要进入区间还是出区间。
但是这个移动中会出现一个问题,移动时候如果跨过 lca 了会出问题,如下图。
我们按照上面步骤从 \((u,v)\) 移到 \((u',v')\)的时候,两个 lca 都被标记了两次,也就是标记状态没有改变,这是错误的。
所以我们要把 lca 放到最后特判,单独更新。就解决问题了。
形象一点就是:因为若是边权这样处理没有问题,所以我们将树上路径点权移到它连向祖先的边上(这时候要删掉 lca 的点权),成功改为边权,然后正常移动,然后再变回点权(要把 lca 点权加回来)。
这甚至是树上带修莫队太毒瘤了。
代码被我吃了。由于是缝合题,代码有点长,但是很好理解。
点击查看代码
/*
* Author: ShaoJia
* Create Time: 2022-08-24 19:46:10
* Last Modified time: 2022-08-25 14:49:42
* Motto: We'll be counting stars.
*/
#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
#define mkp make_pair
#define pb emplace_back
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define ckmx(a,b) a=max(a,b)
#define ckmn(a,b) a=min(a,b)
#define debug(...) cerr<<"#"<<__LINE__<<": "<<__VA_ARGS__<<endl
#define int long long
char buf[1<<21],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read() {
int x=0,f=1;
char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=gc();}
return x*f;
}
//------------------------------------------
const int N=100005,gap=2000,C=16;
int b[N];
struct Que{
int u,v,t,id;
friend bool operator<(Que x,Que y){
if(b[x.u]!=b[y.u]) return b[x.u]<b[y.u];
if(b[x.v]!=b[y.v]) return b[x.v]<b[y.v];
return x.t<y.t;
}
}q[N];
struct Chan{ int x,lst,nxt; }c[N];
vector<int> e[N];
int n,m,qt=0,v[N],w[N],a[N],s[N],st=0;
int f[N][C+1],dep[N],bl=0,tim=0,now,ans[N],vis[N],cnt[N];
void dfs(int rt,int fa){
dep[rt]=dep[fa]+1;
f[rt][0]=fa;
For(i,1,C) f[rt][i]=f[f[rt][i-1]][i-1];
int tmp=st;
s[++st]=rt;
for(int i:e[rt]) if(i!=fa){
dfs(i,rt);
if(st-tmp>gap){
bl++;
while(st!=tmp) b[s[st--]]=bl;
}
}
}
int lca(int x,int y){
int xx,yy;
if(dep[x]<dep[y]) swap(x,y);
Rof(i,C,0){
xx=f[x][i];
if(dep[xx]>=dep[y]) x=xx;
}
if(x==y) return x;
Rof(i,C,0){
xx=f[x][i];
yy=f[y][i];
if(xx!=yy) x=xx,y=yy;
}
return f[x][0];
}
void del(int x){ now-=w[cnt[x]--]*v[x]; }
void add(int x){ now+=w[++cnt[x]]*v[x]; }
void work(int x){
if(vis[x]) del(a[x]); else add(a[x]);
vis[x]^=1;
}
void change(int x,int val){
if(vis[x]) del(a[x]),add(val);
a[x]=val;
}
void mov(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y])
work(x),x=f[x][0];
while(x!=y){
work(x),x=f[x][0],
work(y),y=f[y][0];
}
}
signed main(){
int opt,x,y,tmp;
n=read(),m=read(),tmp=read();
For(i,1,m) v[i]=read();
For(i,1,n) w[i]=read();
For(i,1,n-1){
x=read(),y=read();
e[x].pb(y);
e[y].pb(x);
}
For(i,1,n) s[i]=a[i]=read();
while(tmp--){
opt=read(),x=read(),y=read();
if(!opt) c[++tim]=(Chan){x,s[x],y},s[x]=y;
else qt++,q[qt]=(Que){x,y,tim,qt};
}
dfs(1,0);
if(st){ bl++; while(st) b[s[st--]]=bl; }
sort(q+1,q+1+qt);
int T=0,U=1,V=1;
work(1);
For(i,1,qt){
while(T<q[i].t) T++,change(c[T].x,c[T].nxt);
while(T>q[i].t) change(c[T].x,c[T].lst),T--;
work(lca(U,V));
mov(U,q[i].u);
mov(V,q[i].v);
work(lca(U=q[i].u,V=q[i].v));
ans[q[i].id]=now;
}
For(i,1,qt) printf("%lld\n",ans[i]);
return 0;}
树上撒点
作者:ShaoJia,欢迎分享本文,转载时敬请注明原文来源链接。

浙公网安备 33010602011771号