数据结构 / Datastructure(ACM-Template 2.0)
数据结构
并查集 / DSU
精简版并查集
- 只有路径压缩。(特定情况下会被卡成 \(O(\log n)\))
struct DSU { // join: d[x] = d[y], query: d[x] == d[y]
int a[N];
void init(int n) { iota(a, a + n + 1, 0); }
int fa(int x) { return a[x] == x ? x : a[x] = fa(a[x]); }
int &operator[](int x) { return a[fa(x)]; }
} d;
普通并查集
- 路径压缩 + 启发式合并,\(O(α(n))\),可视为 \(O(1)\)。
struct DSU {
int a[N], sz[N];
void init(int n) {
iota(a, a + n + 1, 0);
fill(sz, sz + n + 1, 1);
}
int fa(int x) {
return a[x] == x ? x : a[x] = fa(a[x]);
}
bool query(int x, int y) { // ask if set x == set y
return fa(x) == fa(y);
}
void join(int x, int y) { // join set x and set y
x = fa(x), y = fa(y);
if (x == y) return;
if (sz[x] > sz[y]) swap(x, y);
a[x] = y; sz[y] += sz[x];
}
int operator[](int x) { return fa(x); }
} d;
种类并查集
struct DSU{
int a[N],r[N];
void init(int n){
repeat(i,0,n+1)a[i]=i,r[i]=0;
}
int plus(int a,int b){ // 关系 a + 关系 b,类似向量相加
if(a==b)return -a;
return a+b;
}
int inv(int a){ // 关系 a 的逆
return -a;
}
int fa(int x){ // 返回根结点
if(a[x]==x)return x;
int f=a[x],ff=fa(f);
r[x]=plus(r[x],r[f]);
return a[x]=ff;
}
bool query(int x,int y){ // 是否存在关系
return fa(x)==fa(y);
}
int R(int x,int y){ // 查找关系
return plus(r[x],inv(r[y]));
}
void join(int x,int y,int r2){ // 按 r2 关系合并
r2=plus(R(y,x),r2);
x=fa(x),y=fa(y);
a[x]=y,r[x]=r2;
}
}d;
可撤销种类并查集
- (维护是否有奇环)
namespace DSU{
int a[N],r[N],sz[N];
vector<pair<int *,int>> rec;
void init(int n){repeat(i,0,n+1)a[i]=i,r[i]=0,sz[i]=1;}
int plus(int a,int b){return a^b;}
int inv(int a){return a;}
int fa(int x){return a[x]==x?x:fa(a[x]);}
int R(int x){return a[x]==x?r[x]:plus(r[x],R(a[x]));} // relation<x,fa(x)>
int R(int x,int y){return plus(R(x),inv(R(y)));} // relation<x,y>
void join(int x,int y,int r2){
r2=plus(R(y,x),r2); x=fa(x),y=fa(y);
if(sz[x]>sz[y])swap(x,y),r2=inv(r2);
rec.push_back({&a[x],a[x]});
rec.push_back({&r[x],r[x]});
rec.push_back({&sz[y],sz[y]});
a[x]=y; r[x]=r2; sz[y]+=sz[x];
}
void undo(){
repeat_back(i,0,rec.size())*rec[i].fi=rec[i].se;
rec.clear();
}
}using namespace DSU;
<补充> 可持久化并查集
- 启发式合并,不能路径压缩,\(O(\log^2 n)\)
struct seg *pl; int segl,segr;
struct seg{
ll a; seg *lc,*rc;
}pool[N*20*3];
pair<seg *,seg *> h[N];
void init(int l,int r){
segl=l,segr=r; pl=pool;
iota(in+l,in+r+1,l); h[0].fi=pl; pl->init(l,r);
fill(in+l,in+r+1,1); h[0].se=++pl; pl->init(l,r);
}
int fa(seg *a,int x){
int t; while((t=a->query(x))!=x)x=t; return x;
}
void join(seg *&a,seg *&sz,int x,int y){ // a=h[i].fi,sz=h[i].se
x=fa(a,x); y=fa(a,y);
if(x!=y){
int sx=sz->query(x),sy=sz->query(y);
if(sx<sy)swap(x,y),swap(sx,sy);
a=update(a,y,x),sz=update(sz,x,sx+sy);
}
}
树状数组 / BIT
普通树状数组
- 单点+区间,修改查询 \(O(\log n)\)
struct BIT {
#define lb(x) (x & -x) // lowbit
static const int s = 0; // min index
ll a[N]; int n;
void init(int _n) { // range = [s, _n]
n = _n - s + 1;
fill(a, a + n + 1, 0);
}
void add(int x, ll k) { // a[x] += k
x += -s + 1;
for (; x <= n; x += lb(x)) a[x] += k;
}
ll sum(int x) { // sum [1, x] // [s, x]
x += -s + 1;
ll ans = 0;
for (; x != 0; x -= lb(x)) ans += a[x];
return ans;
}
ll sum(int l, int r) { // sum [l, r]
return sum(r) - sum(l - 1);
}
} bit;
- 线性时间建树
for (int i=1;i<=n;i++){
scanf("%lld",s+i);
for(int j=1;j<lowbit(i);j<<=1)s[i]+=s[i-j];
}
- 大佬的第 k 小(权值树状数组)
int findkth(int k){
int ans=0,cnt=0;
for(int i=20;i>=0;--i){
ans+=1<<i;
if (ans>=n || cnt+t[ans]>=k)ans-=1<<i;
else cnt+=t[ans];
}
return ans+1;
}
超级树状数组
- 基于树状数组,基本只允许加法,区间+区间,\(O(\log n)\)
struct SPBIT{
BIT a,a1;
void init(){a.init();a1.init();}
void add(ll x,ll y,ll k){
a.add(x,k);
a.add(y+1,-k);
a1.add(x,k*(x-1));
a1.add(y+1,-k*y);
}
ll sum(ll x,ll y){
return y*a.sum(y)-(x-1)*a.sum(x-1)-(a1.sum(y)-a1.sum(x-1));
}
}spbit;
<补充> 二维超级树状数组
- 修改查询 \(O(\log n\cdot\log m)\)
int n,m;
#define lb(x) (x&(-x))
struct BIT{
ll t[N][N]; // 一倍内存吧
void init(){
mst(t,0);
}
void add(int x,int y,ll k){ // 位置(x,y)加上k
// x++,y++; // 如果要从0开始编号
for(int i=x;i<=n;i+=lb(i))
for(int j=y;j<=m;j+=lb(j))
t[i][j]+=k;
}
ll sum(int x,int y){ // 求(1..x,1..y)的和
// x++,y++; // 如果要从0开始编号
ll ans=0;
for(int i=x;i!=0;i-=lb(i))
for(int j=y;j!=0;j-=lb(j))
ans+=t[i][j];
return ans;
}
};
struct SPBIT{
BIT a,ax,ay,axy;
void add(int x,int y,int k){
a.add(x,y,k);
ax.add(x,y,k*x);
ay.add(x,y,k*y);
axy.add(x,y,k*x*y);
}
ll sum(int x,int y){
return a.sum(x,y)*(x*y+x+y+1)
-ax.sum(x,y)*(y+1)
-ay.sum(x,y)*(x+1)
+axy.sum(x,y);
}
void add(int x0,int y0,int x1,int y1,int k){ // 区间修改
add(x0,y0,k);
add(x0,y1+1,-k);
add(x1+1,y0,-k);
add(x1+1,y1+1,k);
}
ll sum(int x0,int y0,int x1,int y1){ // 区间查询
return sum(x1,y1)
-sum(x0-1,y1)
-sum(x1,y0-1)
+sum(x0-1,y0-1);
}
}spbit;
ST 表 / Sparse Table
普通 ST 表
- 编号从 0 开始,初始化 \(O(n\log n)\) 查询 \(O(1)\)
struct ST{
#define logN 21
#define U(x,y) max(x,y)
ll a[N][logN];
void init(int n){
repeat(i,0,n)
a[i][0]=in[i];
repeat(k,1,logN)
repeat(i,0,n-(1<<k)+1)
a[i][k]=U(a[i][k-1],a[i+(1<<(k-1))][k-1]);
}
ll query(int l,int r){
int s=31-__builtin_clz(r-l+1);
return U(a[l][s],a[r-(1<<s)+1][s]);
}
}st;
二维 ST 表
- 编号从 0 开始,初始化 \(O(nm\log n\log m)\) 查询 \(O(1)\)。
struct ST{ // 注意logN=log(N)+2
#define logN 9
#define U(x,y) max(x,y)
int f[N][N][logN][logN],log[N];
ST(){
log[1]=0;
repeat(i,2,N)
log[i]=log[i/2]+1;
}
void build(){
repeat(k,0,logN)
repeat(l,0,logN)
repeat(i,0,n-(1<<k)+1)
repeat(j,0,m-(1<<l)+1){
int &t=f[i][j][k][l];
if(k==0 && l==0)t=a[i][j];
else if(k)
t=U(f[i][j][k-1][l],f[i+(1<<(k-1))][j][k-1][l]);
else
t=U(f[i][j][k][l-1],f[i][j+(1<<(l-1))][k][l-1]);
}
}
int query(int x0,int y0,int x1,int y1){
int k=log[x1-x0+1],l=log[y1-y0+1];
return U(U(U(
f[x0][y0][k][l],
f[x1-(1<<k)+1][y0][k][l]),
f[x0][y1-(1<<l)+1][k][l]),
f[x1-(1<<k)+1][y1-(1<<l)+1][k][l]);
}
}st;
<补充> 猫树
- 编号从 0 开始,初始化 \(O(n\log n)\) 查询 \(O(1)\)
struct cat{
#define U(a,b) max(a,b) // 查询操作
#define a0 0 // 查询操作的零元
#define logN 21
vector<ll> a[logN];
vector<ll> v;
void init(){
repeat(i,0,logN)a[i].clear();
v.clear();
}
void push(ll in){
v.push_back(in);
int n=v.size()-1;
repeat(s,1,logN){
int len=1<<s; int l=n/len*len;
if(n%len==len/2-1){
repeat(i,0,len)a[s].push_back(a0);
repeat_back(i,0,len/2)a[s][l+i]=U(a[s][l+i+1],v[l+i]);
}
if(n%len>=len/2)
a[s][n]=(U(a[s][n-1],v[n]));
}
}
ll query(int l,int r){ // 区间查询
if(l==r)return v[l];
int s=32-__builtin_clz(l^r);
return U(a[s][l],a[s][r]);
}
}tr;
单调队列
- 求所有长度为k的区间中的最大值,线性复杂度
struct MQ{ // 查询就用mq.q.front().first
deque<pii> q; // first:保存的最大值; second:时间戳
void init(){q.clear();}
void push(int x,int k){
static int T=0; T++;
while(!q.empty() && q.back().fi<=x) // max
q.pop_back();
q.push_back({x,T});
while(!q.empty() && q.front().se<=T-k)
q.pop_front();
}
void work(function<int&(int)> a,int n,int k){ // 原地保存,编号从0开始
init();
repeat(i,0,n){
push(a(i),k);
if(i+1>=k)a(i+1-k)=q.front().fi;
}
}
void work(int a[][N],int n,int m,int k){ // 原地保存,编号从0开始
repeat(i,0,n){
init();
repeat(j,0,m){
push(a[i][j],k);
if(j+1>=k)a[i][j+1-k]=q.front().fi;
}
}
m-=k-1;
repeat(j,0,m){
init();
repeat(i,0,n){
push(a[i][j],k);
if(i+1>=k)a[i+1-k][j]=q.front().fi;
}
}
}
}mq;
// 求n*m矩阵中所有k*k连续子矩阵最大值之和 // 编号从1开始
repeat(i,1,n+1)
mq.work([&](int x)->int&{return a[i][x+1];},m,k);
repeat(j,1,m-k+2)
mq.work([&](int x)->int&{return a[x+1][j];},n,k);
ll ans=0; repeat(i,1,n-k+2)repeat(j,1,m-k+2)ans+=a[i][j];
// 或者
mq.work((int(*)[N])&(a[1][1]),n,m,k);
线段树 / Segment Tree
- 基本上适用于所有(线段树能实现的)区间+区间
- 我删了修改运算的零元,加了偷懒状态 (state),
终于能支持赋值操作.jpg - 初始化: init(), 修改查询: tr->sth()
- U(x,y): 查询运算
- a0: 查询运算的零元
- toz(x): 把 x 加载到懒标记
- toa(): 懒标记加载到数据(z别忘了清空)
- a: 数据, z: 懒标记, state: 懒标记使用状态
struct seg {
ll U(ll x, ll y) { return x + y; }
static const ll a0 = 0;
void toz(ll x) { z += x, state = 1; }
void toa() { a += z * (r - l + 1), z = 0, state = 0; }
ll a, z; bool state;
int l, r; seg *lc, *rc;
void init(int _l, int _r, seg *&pl) {
l = _l, r = _r; state = 0; z = 0;
if (l == r) { a = a0; return; }
int m = (l + r) >> 1;
lc = ++pl; lc->init(l, m, pl);
rc = ++pl; rc->init(m + 1, r, pl);
up();
}
void build(ll in[]) {
if (l == r) { a = in[l]; return; }
lc->build(in); rc->build(in);
up();
}
void up() { a = U(lc->a, rc->a); }
void down() {
if (!state) return;
if (l < r) { lc->toz(z); rc->toz(z); }
toa();
}
void update(int x, int y, ll k) {
if (x > r || y < l) { down(); return; }
if (x <= l && y >= r) { toz(k); down(); return; }
down();
lc->update(x, y, k);
rc->update(x, y, k);
up();
}
ll query(int x, int y) {
if (x > r || y < l) return a0;
down();
if (x <= l && y >= r) return a;
return U(lc->query(x, y), rc->query(x, y));
}
} tr[N * 2], *pl;
void init(int l, int r, ll in[] = nullptr) {
pl = tr;
tr->init(l, r, pl);
if (in) tr->build(in);
}
- 保存一下旧版线段树
struct seg {
#define U(x, y) (x + y)
#define a0 0
void toz(ll x) { z += x, state = 1; }
void toa() { a += z * (r - l + 1), z = 0, state = 0; }
ll a, z; bool state;
int l, r; seg *lc, *rc;
void init(int, int);
void up() { a = U(lc->a, rc->a); }
void down() {
if (!state) return;
if (l < r) { lc->toz(z); rc->toz(z); }
toa();
}
void update(int x, int y, ll k) {
if (x > r || y < l) { down(); return; }
if (x <= l && y >= r) { toz(k); down(); return; }
down();
lc->update(x, y, k);
rc->update(x, y, k);
up();
}
ll query(int x, int y) {
if (x > r || y < l) return a0;
down();
if (x <= l && y >= r) return a;
return U(lc->query(x, y), rc->query(x, y));
}
} tr[N * 2], *pl;
void seg::init(int _l, int _r) {
l = _l, r = _r; state = 0; z = 0;
if (l == r) { a = in[l]; return; }
int m = (l + r) >> 1;
lc = ++pl; lc->init(l, m);
rc = ++pl; rc->init(m + 1, r);
up();
}
void init(int l, int r) {
pl = tr;
tr->init(l, r);
}
<补充> 高拓展性线段树
- 例:luogu P3373 线段树 2,支持区间加、区间乘、区间和查询
struct Z{ // lazy tag
int x,y; explicit Z(int x=1,int y=0):x(x),y(y){}
void push(Z b,int l,int r){
x=(x*b.x)%mod;
y=(y*b.x+b.y)%mod;
}
};
struct A{ // seg tag
int x; explicit A(int x=0):x(x){}
void push(Z b,int l,int r){
x=(x*b.x+b.y*(r-l+1))%mod;
}
A operator+(A b)const{return A((x+b.x)%mod);}
};
struct seg{
A a; Z z; bool state; int l,r; seg *lc,*rc;
void init(int,int);
void up(){a=lc->a+rc->a;}
void toz(Z x){z.push(x,l,r),state=1;}
void down(){
if(!state)return;
if(l<r){lc->toz(z); rc->toz(z);}
a.push(z,l,r),z=Z(),state=0;
}
void update(int x,int y,Z k){
if(x>r || y<l){down(); return;}
if(x<=l && y>=r){toz(k); down(); return;}
down();
lc->update(x,y,k);
rc->update(x,y,k);
up();
}
A query(int x,int y){
if(x>r || y<l)return A();
down(); if(x<=l && y>=r)return a;
return lc->query(x,y)+rc->query(x,y);
}
}tr[N*2],*pl;
int in[N];
void seg::init(int _l,int _r){
l=_l,r=_r; state=0;
if(l==r){a=in[l]; return;}
int m=(l+r)>>1;
lc=++pl; lc->init(l,m);
rc=++pl; rc->init(m+1,r);
up();
}
void init(int l,int r){
pl=tr; tr->init(l,r);
}
<补充> 权值线段树(动态开点 线段树合并 线段树分裂)
- 初始 n 个线段树,支持对某个线段树插入权值、合并两个线段树、查询某个线段树第k小数
- 编号从 1 开始,\(O(n\log n)\)
DSU d;
struct seg{
seg *lc,*rc; int sz;
}tr[N<<5],*pl,*rt[N];
#define LL lc,l,m
#define RR rc,m+1,r
int size(seg *s){return s?s->sz:0;}
seg *newnode(){*pl=seg(); return pl++;}
void up(seg *s){s->sz=size(s->lc)+size(s->rc);}
void insert(seg *&s,int l,int r,int v,int num=1){ // insert v, (s=rt[d[x]])
if(!s)s=newnode(); s->sz+=num;
if(l==r)return;
int m=(l+r)/2;
if(v<=m)insert(s->LL,v,num);
else insert(s->RR,v,num);
}
seg *merge(seg *a,seg *b,int l,int r){ // private, return the merged tree
if(!a)return b; if(!b)return a;
a->sz+=b->sz;
if(l==r)return a;
int m=(l+r)/2;
a->lc=merge(a->lc,b->LL);
a->rc=merge(a->rc,b->RR);
return a;
}
void merge(int x,int y,int l,int r){ // merge tree x and y
if(d[x]==d[y])return;
rt[d[x]]=merge(rt[d[x]],rt[d[y]],l,r);
d[y]=d[x];
}
int kth(seg *s,int l,int r,int k){ // kth in s, (k=1,2,...,sz, s=rt[d[x]])
if(l==r)return l;
int m=(l+r)/2,lv=size(s->lc);
if(k<=lv)return kth(s->LL,k);
else return kth(s->RR,k-lv);
}
int query(seg *s,int l,int r,int x,int y){ // count the numbers between [x,y] (s=rt[d[x]])
if(!s || x>r || y<l)return 0;
if(x<=l && y>=r)return s->sz;
int m=(l+r)/2;
return query(s->LL,x,y)+query(s->RR,x,y);
}
void split(seg *&s,int l,int r,int x,int y,seg *&t){ // the numbers between [x,y] trans from s to t, (s=rt[d[x]], t=rt[d[y]])
if(!s || x>r || y<l)return;
if(x<=l && y>=r){t=merge(s,t,l,r); s=0; return;}
if(!t)t=newnode();
int m=(l+r)/2;
split(s->LL,x,y,t->lc);
split(s->RR,x,y,t->rc);
up(s); up(t);
}
void init(int n){ // create n trees
pl=tr; d.init(n);
fill(rt,rt+n+1,nullptr);
}
<补充> zkw 线段树
- 单点 + 区间,编号从 0 开始,建树 \(O(n)\) 修改查询 \(O(\log n)\)。(存在区间 + 区间的 zkw,但是看起来不太好用)
- 代码量和常数都和树状数组差不多。
struct zkw {
ll U(ll x, ll y) { return x + y; }
const ll a0 = 0;
int n; ll a[N * 4];
void init(int inn, ll in[] = nullptr) { // A[x] = a0 or in[x]
n = 1; while (n < inn) n <<= 1;
fill(a + n, a + n * 2, a0);
if (in) repeat (i, 0, inn) a[n + i] = in[i];
repeat_back (i, 1, n) up(i);
}
void up(int x) { // private
a[x] = U(a[x * 2], a[(x * 2) + 1]);
}
void assign(int x, ll k) { // A[x] = k
a[x += n] = k;
while (x >>= 1) up(x);
}
void add(int x, ll k) { // A[x] += k
a[x += n] += k;
while (x >>= 1) up(x);
}
ll query(int l, int r) { // U(A[l, r])
ll ans = a0; l += n - 1, r += n + 1;
while (l + 1 < r){
if (~l & 1) ans = U(ans, a[l + 1]);
if ( r & 1) ans = U(ans, a[r - 1]);
l >>= 1, r >>= 1;
}
return ans;
}
} tr;
<补充> 可持久化数组
- 单点修改并创建新版本:
h[top]=update(h[i],x,v);
(每次 \(O(\log n)\) 额外内存) - 单点查询
h[i]->query(x);
- 初始化 \(O(n)\),修改查询 \(O(\log n)\)
struct seg *pl; int segl,segr;
struct seg{
ll a; seg *lc,*rc;
ll query(int x,int l=segl,int r=segr){
if(l==r)return a;
int m=(l+r)>>1;
if(x<=m)return lc->query(x,l,m);
else return rc->query(x,m+1,r);
}
friend seg *update(seg *u,int x,ll v,int l=segl,int r=segr){
*++pl=*u; u=pl;
if(l==r)u->a=v;
else{
int m=(l+r)>>1;
if(x<=m)u->lc=update(u->lc,x,v,l,m);
else u->rc=update(u->rc,x,v,m+1,r);
}
return u;
}
void init(int l,int r){
if(l==r){a=in[l]; return;}
int m=(l+r)>>1;
lc=++pl; lc->init(l,m);
rc=++pl; rc->init(m+1,r);
}
}pool[N*20],*h[N]; // h: versions
void init(int l,int r){
segl=l,segr=r;
pl=pool; pl->init(l,r); h[0]=pl;
}
<补充> 主席树
- 初始化
init(l,r)
,版本复制his[i]=his[j]
(先复制再修改) - 单点修改
update(his[i],x,k)
,区间询问query(his[i],x,y)
- 权值线段树 \(his[i]\setminus his[j]\) 询问 k 小
kth(his[i],his[j],k)
- 静态区间k小:构造 \(his[i]\) 为区间 \([1,i]\) 的权值线段树,
kth(his[r],his[l-1],k)
即区间k小 - 修改询问 \(O(\log n)\)
namespace seg{
struct{
ll x; int l,r;
}a[N<<5];
int his[N],cnt,l0,r0;
void init(int l,int r){
l0=l,r0=r;
cnt=0;
}
void update(int &u,int x,ll k,int l=l0,int r=r0){ // tr[u][x]+=k
a[++cnt]=a[u]; u=cnt;
if(l==r){a[u].x+=k; return;}
int m=(l+r)/2;
if(x<=m)update(a[u].l,x,k,l,m);
else update(a[u].r,x,k,m+1,r);
a[u].x=a[a[u].l].x+a[a[u].r].x;
}
ll query(int u,int x,int y,int l=l0,int r=r0){ // sum(tr[u][x..y])
if(!u || x>r || y<l)return 0;
if(x<=l && y>=r)return a[u].x;
int m=(l+r)/2;
return query(a[u].l,x,y,l,m)+query(a[u].r,x,y,m+1,r);
}
ll kth(int u,int v,int k,int l=l0,int r=r0){ // kth in (tr[u]-tr[v])[x..y]
if(l==r)return l;
int m=(l+r)/2,lv=a[a[u].l].x-a[a[v].l].x;
if(k<=lv)return kth(a[u].l,a[v].l,k,l,m);
else return kth(a[u].r,a[v].r,k-lv,m+1,r);
}
}using namespace seg;
<补充> 李超线段树
- 支持插入线段、查询所有线段与 \(x=x_0\) 交点最高的那条线段
- 修改 \(O(\log^2n)\),查询 \(O(\log n)\)
int funx; // 这是y()的参数
struct func{
lf k,b; int id;
lf y()const{return k*funx+b;} // funx点处的高度
bool operator<(const func &b)const{
return make_pair(y(),-id)<make_pair(b.y(),-b.id);
}
};
struct seg{ // 初始化init()更新update()查询query(),func::y()是高度
func a;
int l,r;
seg *ch[2];
void init(int,int);
void push(func d){
funx=(l+r)/2;
if(a<d)swap(a,d); // 这个小于要用funx
if(l==r)return;
ch[d.k>a.k]->push(d);
}
void update(int x,int y,const func &d){ // 更新[x,y]区间
x=max(x,l); y=min(y,r); if(x>y)return;
if(x==l && y==r)push(d);
else{
ch[0]->update(x,y,d);
ch[1]->update(x,y,d);
}
}
const func &query(int x){ // 询问
funx=x;
if(l==r)return a;
const func &b=ch[(l+r)/2<x]->query(x);
return max(a,b); // 这个max要用funx
}
}tr[N*2],*pl;
void seg::init(int _l,int _r){
l=_l,r=_r; a={0,-inf,-1}; // 可能随题意改变
if(l==r)return;
int m=(l+r)/2;
(ch[0]=++pl)->init(l,m);
(ch[1]=++pl)->init(m+1,r);
}
void init(int l,int r){
pl=tr;
tr->init(l,r);
}
void add(int x0,int y0,int x1,int y1){ // 线段处理并更新
if(x0>x1)swap(x0,x1),swap(y0,y1);
lf k,b;
if(x0==x1)k=0,b=max(y0,y1);
else{
k=lf(y1-y0)/(x1-x0);
b=y0-k*x0;
}
id++;
tr->update(x0,x1,{k,b,id});
}
<补充> 吉老师线段树 / Segbeats
区间最值操作
- 区间取min,区间max,区间和,\(O(n\log n)\)
int in[N],n;
struct seg{
#define lc (u*2)
#define rc (u*2+1)
int mx[N<<2],se[N<<2],cnt[N<<2],tag[N<<2];
ll sum[N<<2];
void up(int u){ // private
int x=lc,y=rc;
sum[u]=sum[x]+sum[y];
if(mx[x]==mx[y]){
mx[u]=mx[x];
se[u]=max(se[x],se[y]);
cnt[u]=cnt[x]+cnt[y];
}
else{
if(mx[x]<mx[y])swap(x,y);
mx[u]=mx[x];
se[u]=max(se[x],mx[y]);
cnt[u]=cnt[x];
}
}
void pushtag(int u,int tg){ // private
if(mx[u]<=tg)return;
sum[u]+=(1ll*tg-mx[u])*cnt[u];
mx[u]=tag[u]=tg;
}
void down(int u){ // private
if(tag[u]==-1)return;
pushtag(lc,tag[u]),pushtag(rc,tag[u]);
tag[u]=-1;
}
void build(int u=1,int l=1,int r=n){
tag[u]=-1;
if(l==r){
sum[u]=mx[u]=in[l],se[u]=-1,cnt[u]=1;
return;
}
int m=(l+r)>>1;
build(lc,l,m),build(rc,m+1,r);
up(u);
}
void tomin(int x,int y,int v,int u=1,int l=1,int r=n){
if(x>r || l>y || mx[u]<=v)return;
if(x<=l && r<=y && se[u]<v)return pushtag(u,v);
int m=(l+r)>>1; down(u);
tomin(x,y,v,lc,l,m);
tomin(x,y,v,rc,m+1,r);
up(u);
}
int qmax(int x,int y,int u=1,int l=1,int r=n){
if(x<=l && r<=y)return mx[u];
if(x>r || l>y)return -inf;
int m=(l+r)>>1; down(u);
return max(qmax(x,y,lc,l,m),qmax(x,y,rc,m+1,r));
}
ll qsum(int x,int y,int u=1,int l=1,int r=n){
if(x<=l && r<=y)return sum[u];
if(x>r || l>y)return 0;
int m=(l+r)>>1; down(u);
return qsum(x,y,lc,l,m)+qsum(x,y,rc,m+1,r);
}
}tr;
- 区间取min,区间取max,区间加,区间min,区间max,区间和,\(O(n\log^2 n)\)
int in[N],n;
struct seg{
#define lc (u*2)
#define rc (u*2+1)
struct node{
int mx,mx2,mn,mn2,cmx,cmn,tmx,tmn,tad;
ll sum;
};
node t[N<<2];
void up(int u){ // private
int x=lc,y=rc;
t[u].sum=t[x].sum+t[y].sum;
if(t[x].mx==t[y].mx){
t[u].mx=t[x].mx,t[u].cmx=t[x].cmx+t[y].cmx;
t[u].mx2=max(t[x].mx2,t[y].mx2);
}
else{
if(t[x].mx<t[y].mx)swap(x,y);
t[u].mx=t[x].mx,t[u].cmx=t[x].cmx;
t[u].mx2=max(t[x].mx2,t[y].mx);
}
if(t[x].mn==t[y].mn){
t[u].mn=t[x].mn,t[u].cmn=t[x].cmn+t[y].cmn;
t[u].mn2=min(t[x].mn2,t[y].mn2);
}
else{
if(t[x].mn>t[y].mn)swap(x,y);
t[u].mn=t[x].mn,t[u].cmn=t[x].cmn;
t[u].mn2=min(t[x].mn2,t[y].mn);
}
}
void push_add(int u,int l,int r,int v){ // private
t[u].sum+=(r-l+1ll)* v;
t[u].mx+=v,t[u].mn+=v;
if(t[u].mx2!=-inf)t[u].mx2+=v;
if(t[u].mn2!=inf)t[u].mn2+=v;
if(t[u].tmx!=-inf)t[u].tmx+=v;
if(t[u].tmn!=inf)t[u].tmn+=v;
t[u].tad+=v;
}
void push_min(int u,int tg){ // private
if(t[u].mx<=tg)return;
t[u].sum+=(tg*1ll-t[u].mx)*t[u].cmx;
if(t[u].mn2==t[u].mx)t[u].mn2=tg;
if(t[u].mn==t[u].mx)t[u].mn=tg;
if(t[u].tmx>tg)t[u].tmx=tg;
t[u].mx=tg,t[u].tmn=tg;
}
void push_max(int u,int tg){ // private
if(t[u].mn>tg)return;
t[u].sum+=(tg*1ll-t[u].mn)*t[u].cmn;
if(t[u].mx2==t[u].mn)t[u].mx2=tg;
if(t[u].mx==t[u].mn)t[u].mx=tg;
if(t[u].tmn<tg)t[u].tmn=tg;
t[u].mn=tg,t[u].tmx=tg;
}
void down(int u,int l,int r){ // private
const int m=(l+r)>>1;
if(t[u].tad)
push_add(lc,l,m,t[u].tad),push_add(rc,m+1,r,t[u].tad);
if(t[u].tmx!=-inf)push_max(lc,t[u].tmx),push_max(rc,t[u].tmx);
if(t[u].tmn!=inf)push_min(lc,t[u].tmn),push_min(rc,t[u].tmn);
t[u].tad=0,t[u].tmx=-inf,t[u].tmn=inf;
}
void build(int u=1,int l=1,int r=n){
t[u].tmn=inf,t[u].tmx=-inf;
if(l==r){
t[u].sum=t[u].mx=t[u].mn=in[l];
t[u].mx2=-inf,t[u].mn2=inf;
t[u].cmx=t[u].cmn=1;
return;
}
int m=(l+r)>>1;
build(lc,l,m),build(rc,m+1,r);
up(u);
}
void add(int x,int y,int v,int u=1,int l=1,int r=n){
if(y<l || r<x)return;
if(x<=l && r<=y)return push_add(u,l,r,v);
int m=(l+r)>>1;
down(u,l,r);
add(x,y,v,lc,l,m),add(x,y,v,rc,m+1,r);
up(u);
}
void tomin(int x,int y,int v,int u=1,int l=1,int r=n){
if(y<l || r<x || t[u].mx<=v)return;
if(x<=l && r<=y && t[u].mx2<v)return push_min(u,v);
int m=(l+r)>>1;
down(u,l,r);
tomin(x,y,v,lc,l,m),tomin(x,y,v,rc,m+1,r);
up(u);
}
void tomax(int x,int y,int v,int u=1,int l=1,int r=n){
if(y<l || r<x || t[u].mn>=v)return;
if(x<=l && r<=y && t[u].mn2>v)return push_max(u,v);
int m=(l+r)>>1;
down(u,l,r);
tomax(x,y,v,lc,l,m),tomax(x,y,v,rc,m+1,r);
up(u);
}
ll qsum(int x,int y,int u=1,int l=1,int r=n){
if(y<l || r<x)return 0;
if(x<=l && r<=y)return t[u].sum;
int m=(l+r)>>1;
down(u,l,r);
return qsum(x,y,lc,l,m)+qsum(x,y,rc,m+1,r);
}
ll qmax(int x,int y,int u=1,int l=1,int r=n){
if(y<l || r<x)return -inf;
if(x<=l && r<=y)return t[u].mx;
int m=(l+r)>>1;
down(u,l,r);
return max(qmax(x,y,lc,l,m),qmax(x,y,rc,m+1,r));
}
ll qmin(int x,int y,int u=1,int l=1,int r=n){
if(y<l || r<x)return inf;
if(x<=l && r<=y)return t[u].mn;
int m=(l+r)>> 1;
down(u,l,r);
return min(qmin(x,y,lc,l,m),qmin(x,y,rc,m+1,r));
}
}tr;
区间历史最值
- 模板题,支持区间加、区间取min、区间和、区间max、区间历史max(\(\max_{i\in[1,t]j\in [l,r]}h_i[j]\)),\(O(\log^2n)\)
struct Segbeats { // init: build(1, 1, n, a)
struct Node {
int l, r;
int mx, mxh, se, cnt;
ll sum;
int a1, a1h, a2, a2h;
} a[N * 4];
#define lc (u << 1)
#define rc (u << 1 | 1)
void up(int u) { // private
a[u].sum = a[lc].sum + a[rc].sum;
a[u].mxh = max(a[lc].mxh, a[rc].mxh);
if (a[lc].mx == a[rc].mx) {
a[u].mx = a[lc].mx;
a[u].se = max(a[lc].se, a[rc].se);
a[u].cnt = a[lc].cnt + a[rc].cnt;
} else if (a[lc].mx > a[rc].mx) {
a[u].mx = a[lc].mx;
a[u].se = max(a[lc].se, a[rc].mx);
a[u].cnt = a[lc].cnt;
} else {
a[u].mx = a[rc].mx;
a[u].se = max(a[lc].mx, a[rc].se);
a[u].cnt = a[rc].cnt;
}
}
void update(int u, int k1, int k1h, int k2, int k2h) { // private
a[u].sum += 1ll * k1 * a[u].cnt + 1ll * k2 * (a[u].r - a[u].l + 1 - a[u].cnt);
a[u].mxh = max(a[u].mxh, a[u].mx + k1h);
a[u].a1h = max(a[u].a1h, a[u].a1 + k1h);
a[u].mx += k1, a[u].a1 += k1;
a[u].a2h = max(a[u].a2h, a[u].a2 + k2h);
if (a[u].se != -INF) a[u].se += k2;
a[u].a2 += k2;
}
void down(int u) { // private
int tmp = max(a[lc].mx, a[rc].mx);
if (a[lc].mx == tmp)
update(lc, a[u].a1, a[u].a1h, a[u].a2, a[u].a2h);
else
update(lc, a[u].a2, a[u].a2h, a[u].a2, a[u].a2h);
if (a[rc].mx == tmp)
update(rc, a[u].a1, a[u].a1h, a[u].a2, a[u].a2h);
else
update(rc, a[u].a2, a[u].a2h, a[u].a2, a[u].a2h);
a[u].a1 = a[u].a1h = a[u].a2 = a[u].a2h = 0;
}
void build(int u, int l, int r, int in[]) {
a[u].l = l, a[u].r = r;
a[u].a1 = a[u].a1h = a[u].a2 = a[u].a2h = 0;
if (l == r) {
a[u].sum = a[u].mxh = a[u].mx = in[l];
a[u].se = -INF, a[u].cnt = 1;
return;
}
int mid = l + r >> 1;
build(lc, l, mid, in);
build(rc, mid + 1, r, in);
up(u);
}
void add(int u, int x, int y, int k) {
if (a[u].l > y || a[u].r < x) return;
if (x <= a[u].l && a[u].r <= y) {
update(u, k, k, k, k);
return;
}
down(u);
add(lc, x, y, k), add(rc, x, y, k);
up(u);
}
void tomin(int u, int x, int y, int k) {
if (a[u].l > y || a[u].r < x || k >= a[u].mx) return;
if (x <= a[u].l && a[u].r <= y && k > a[u].se) {
update(u, k - a[u].mx, k - a[u].mx, 0, 0);
return;
}
down(u);
tomin(lc, x, y, k), tomin(rc, x, y, k);
up(u);
}
ll qsum(int u, int x, int y) {
if (a[u].l > y || a[u].r < x) return 0;
if (x <= a[u].l && a[u].r <= y) return a[u].sum;
down(u);
return qsum(lc, x, y) + qsum(rc, x, y);
}
int qmax(int u, int x, int y) {
if (a[u].l > y || a[u].r < x) return -INF;
if (x <= a[u].l && a[u].r <= y) return a[u].mx;
down(u);
return max(qmax(lc, x, y), qmax(rc, x, y));
}
int qhmax(int u, int x, int y) {
if (a[u].l > y || a[u].r < x) return -INF;
if (x <= a[u].l && a[u].r <= y) return a[u].mxh;
down(u);
return max(qhmax(lc, x, y), qhmax(rc, x, y));
}
#undef lc
#undef rc
} sgt;
堆
可删堆
- 原理:双堆模拟。
struct Heap{
priority_queue<int> a,b; // heap=a-b
void push(int x){a.push(x);}
void erase(int x){b.push(x);}
int top(){
while(!b.empty() && a.top()==b.top())
a.pop(),b.pop();
return a.top();
}
void pop(){
while(!b.empty() && a.top()==b.top())
a.pop(),b.pop();
a.pop();
}
int top2(){ // 次大值
int t=top(); pop();
int ans=top(); push(t);
return ans;
}
int size(){return a.size()-b.size();}
};
左偏树
- 万年不用,\(O(\log n)\)。
struct leftist{ // 编号从 1 开始,因为空的左右儿子会指向 0
#define lc LC[x]
#define rc RC[x]
vector<int> val,dis,exist,dsu,LC,RC;
void init(){add(0);dis[0]=-1;}
void add(int v){
int t=val.size();
val.pb(v);
dis.pb(0);
exist.pb(1);
dsu.pb(t);
LC.pb(0);
RC.pb(0);
}
int top(int x){
return dsu[x]==x?x:dsu[x]=top(dsu[x]);
}
void join(int x,int y){
if(exist[x] && exist[y] && top(x)!=top(y))
merge(top(x),top(y));
}
int merge(int x,int y){
if(!x || !y)return x+y;
if(val[x]<val[y]) // 大根堆
swap(x,y);
rc=merge(rc,y);
if(dis[lc]<dis[rc])
swap(lc,rc);
dsu[lc]=dsu[rc]=dsu[x]=x;
dis[x]=dis[rc]+1;
return x;
}
void pop(int x){
x=top(x);
exist[x]=0;
dsu[lc]=lc;
dsu[rc]=rc;
dsu[x]=merge(lc,rc); // 指向x的dsu也能正确指向top
}
#undef lc
#undef rc
}lt;
// 添加元素lt.add(v),位置是lt.val.size()-1
// 是否未被pop:lt.exist(x)
// 合并:lt.join(x,y)
// 堆顶:lt.val[lt.top(x)]
// 弹出:lt.pop(x)
struct node{
int v,dis;
node *l,*r;
}pool[N],*pl,*tr[N];
int dis(node *u){return u?u->dis:0;}
node *newnode(){*pl=node(); return pl++;}
void init(){
pl=pool;
}
node *merge(node *x,node *y){
if(!x)return y; if(!y)return x;
if(x->v < y->v)swap(x,y);
x->r=merge(x->r,y);
if(dis(x->l) < dis(x->r))swap(x->l,x->r);
x->dis=dis(x->r)+1;
return x;
}
void pop(node *&u){u=merge(u->l,u->r);}
int top(node *u){return u->v;}
平衡树
无旋 Treap
- 普通平衡树按 v 分裂,文艺平衡树按 sz 分裂。
insert,erase
操作在普通平衡树中,push_back,output(dfs)
在文艺平衡树中。build
(笛卡尔树线性构建) 在普通平衡树中。- 普通平衡树
struct treap{
struct node{
int pri,v,sz;
node *l,*r;
node(int _v){pri=rnd(); v=_v; l=r=0; sz=1;}
node(){}
friend int size(node *u){return u?u->sz:0;}
void up(){sz=1+size(l)+size(r);}
friend pair<node *,node *> split(node *u,int key){ // 按v分裂
if(u==0)return {0,0};
if(key<u->v){
auto o=split(u->l,key);
u->l=o.se; u->up();
return {o.fi,u};
}
else{
auto o=split(u->r,key);
u->r=o.fi; u->up();
return {u,o.se};
}
}
friend node *merge(node *x,node *y){
if(x==0)return y;
if(y==0)return x;
if(x->pri>y->pri){
x->r=merge(x->r,y); x->up();
return x;
}
else{
y->l=merge(x,y->l); y->up();
return y;
}
}
int find_by_order(int ord){
if(ord==size(l))return v;
if(ord<size(l))return l->find_by_order(ord);
else return r->find_by_order(ord-size(l)-1);
}
}pool[N],*pl,*rt;
void init(){
pl=pool;
rt=0;
// top=0;
}
/*
node *stk[N]; int top;
void build(int pri,int v){ // v is nondecreasing
node *i=++pl; *i=node(v); i->pri=pri;
int cur=top;
while(cur && stk[cur]->pri<i->pri)cur--;
if(cur<top)i->l=stk[cur+1],up(i);
if(cur)stk[cur]->r=i,up(stk[cur]); else rt=i;
stk[++cur]=i;
top=cur;
}
*/
void insert(int key){
auto o=split(rt,key);
*++pl=node(key);
o.fi=merge(o.fi,pl);
rt=merge(o.fi,o.se);
}
void erase_all(int key){
auto o=split(rt,key-1),s=split(o.se,key);
rt=merge(o.fi,s.se);
}
void erase_one(int key){
auto o=split(rt,key-1),s=split(o.se,key);
rt=merge(o.fi,merge(merge(s.fi->l,s.fi->r),s.se));
}
int order(int key){
auto o=split(rt,key-1);
int ans=size(o.fi);
rt=merge(o.fi,o.se);
return ans;
}
int operator[](int x){
return rt->find_by_order(x);
}
int lower_bound(int key){
auto o=split(rt,key-1);
int ans=o.se->find_by_order(0);
rt=merge(o.fi,o.se);
return ans;
}
int nxt(int key){return lower_bound(key+1);}
}tr;
// if(opt==1)tr.insert(x);
// if(opt==2)tr.erase_one(x);
// if(opt==3)cout<<tr.order(x)+1<<endl; // x的排名
// if(opt==4)cout<<tr[x-1]<<endl; // 排名为x
// if(opt==5)cout<<tr[tr.order(x)-1]<<endl; // 前驱
// if(opt==6)cout<<tr.nxt(x)<<endl; // 后继
- 文艺平衡树,
tag
表示翻转子树(区间)
struct treap{
struct node{
int pri,v,sz,tag;
node *l,*r;
node(int _v){pri=(int)rnd(); v=_v; l=r=0; sz=1; tag=0;}
node(){}
friend int size(node *u){return u?u->sz:0;}
void up(){sz=1+size(l)+size(r);}
void down(){
if(tag){
swap(l,r);
if(l)l->tag^=1;
if(r)r->tag^=1;
tag=0;
}
}
friend pair<node *,node *> split(node *u,int key){ // 按sz分裂
if(u==0)return {0,0};
u->down();
if(key<size(u->l)){
auto o=split(u->l,key);
u->l=o.se; u->up();
return {o.fi,u};
}
else{
auto o=split(u->r,key-size(u->l)-1);
u->r=o.fi; u->up();
return {u,o.se};
}
}
friend node *merge(node *x,node *y){
if(x==0 || y==0)return max(x,y);
if(x->pri>y->pri){
x->down();
x->r=merge(x->r,y); x->up();
return x;
}
else{
y->down();
y->l=merge(x,y->l); y->up();
return y;
}
}
}pool[N],*pl,*rt;
void init(){
pl=pool;
rt=0;
}
void push_back(int v){
*++pl=node(v);
rt=merge(rt,pl);
}
void add_tag(int l,int r){ // 编号从0开始
node *a,*b,*c;
tie(a,b)=split(rt,l-1);
tie(b,c)=split(b,r-l);
if(b)b->tag^=1;
rt=merge(a,merge(b,c));
}
void output(node *u){
if(u==0)return; u->down();
output(u->l); cout<<u->v<<' '; output(u->r);
}
}tr;
带懒标记 Treap
struct treap{
struct node{
int pri,sz; ll v,tag,s;
node *l,*r;
node(int _v){pri=(int)rnd(); v=s=_v; l=r=0; sz=1; tag=0;}
node(){}
friend int size(node *u){return u?u->sz:0;}
friend ll sum(node *u){return u?(u->down(),u->s):0;}
void up(){ // private
sz=1+size(l)+size(r);
s=v+sum(l)+sum(r);
}
void down(){ // private
if(tag){
v+=tag; s+=sz*tag;
if(l)l->tag+=tag;
if(r)r->tag+=tag;
tag=0;
}
}
friend pair<node *,node *> split(node *u,int key){ // private
if(u==0)return {0,0};
u->down();
if(key<size(u->l)){
auto o=split(u->l,key);
u->l=o.se; u->up();
return {o.fi,u};
}
else{
auto o=split(u->r,key-size(u->l)-1);
u->r=o.fi; u->up();
return {u,o.se};
}
}
friend node *merge(node *x,node *y){ // private
if(x==0 || y==0)return max(x,y);
if(x->pri>y->pri){
x->down();
x->r=merge(x->r,y); x->up();
return x;
}
else{
y->down();
y->l=merge(x,y->l); y->up();
return y;
}
}
}pool[N],*pl,*rt;
void init(){
pl=pool;
rt=0;
}
void push_back(int v){
*++pl=node(v);
rt=merge(rt,pl);
}
void add_tag(int l,int r,ll tag){
node *a,*b,*c;
tie(a,b)=split(rt,l-1);
tie(b,c)=split(b,r-l);
if(b)b->tag+=tag;
rt=merge(a,merge(b,c));
}
ll query(int l,int r){
node *a,*b,*c;
tie(a,b)=split(rt,l-1);
tie(b,c)=split(b,r-l);
ll ans=sum(b);
rt=merge(a,merge(b,c));
return ans;
}
void output(node *u){
if(u==0)return; u->down();
output(u->l); cout<<u->v<<' '; output(u->r);
}
}tr;
<补充> 可持久化 Treap
- 其他函数从
treap
板子里照搬(但是把rt
作为参数) h[i]=h[j]
就是克隆- 普通平衡树
struct node *pl;
struct node{
int pri,v,sz;
node *l,*r;
node(int _v){pri=rnd(); v=_v; l=r=0; sz=1;}
node(){}
friend int size(node *u){return u?u->sz:0;}
void up(){sz=1+size(l)+size(r);}
friend pair<node *,node *> split(node *u,int key){
if(u==0)return {0,0};
node *w=++pl; *w=*u;
if(key<u->v){
auto o=split(u->l,key);
w->l=o.se; w->up();
return {o.fi,w};
}
else{
auto o=split(u->r,key);
w->r=o.fi; w->up();
return {w,o.se};
}
}
friend node *merge(node *x,node *y){
if(x==0)return y;
if(y==0)return x;
node *w=++pl;
if(x->pri>y->pri){
*w=*x;
w->r=merge(x->r,y);
}
else{
*w=*y;
w->l=merge(x,y->l);
}
w->up();
return w;
}
}pool[N*60],*h[N];
K-D Tree
- K-D tree 可以维护多维空间的点集,用替罪羊树的方法保证复杂度。
- 建树、询问近邻参考第二段代码。
- 模板题,支持在线在 (x, y) 处插入值、查询二维区间和。
- 插入的复杂度为 \(O(\log n)\)。
- 二维区间查询最坏复杂度为 \(O(n^{1-\tfrac 1 k})=O(\sqrt n)\)。
- 询问近邻等很多骚操作的最坏复杂度为 \(O(n)\),最好用别的算法替代。
struct kdt{
#define U(x,y) ((x)+(y))
#define U0 0
struct range{
int l,r;
range operator|(range b)const{
return {min(l,b.l),max(r,b.r)};
}
bool out(range b){
return l>b.r || b.l>r;
}
bool in(range b){
return b.l<=l && r<=b.r;
}
};
struct node{
int x,y,a; // (x,y): coordinate, a: value
int s,d,sz; // s: sum of value in subtree, d: cut direction, sz: size of subtree
range xx,yy; // xx/yy: range of coordinate x/y
node *l,*r; // left/right child
void up(){
sz=l->sz+r->sz+1;
s=U(a,U(l->s,r->s));
xx=range{x,x} | l->xx | r->xx;
yy=range{y,y} | l->yy | r->yy;
}
node *&ch(int px,int py){ // in which child
if(d==0)return px<x ? l : r;
else return py<y ? l : r;
}
node(){
sz=0; a=s=U0;
xx=yy={inf,-inf};
l=r=Null;
}
node(int x_,int y_,int a_){
x=x_,y=y_,a=a_;
l=r=Null;
up();
}
}*rt;
static node Null[N],*pl;
vector<node *> cache;
void init(){ // while using kdtrees, notice Null is static
rt=pl=Null;
}
node *build(node **l,node **r,int d){ // private
if(l>=r)return Null;
node **mid=l+(r-l)/2;
if(d==0)
nth_element(l,mid,r,[&](node *a,node *b){
return a->x < b->x;
});
else
nth_element(l,mid,r,[&](node *a,node *b){
return a->y < b->y;
});
node *u=*mid;
u->d=d;
u->l=build(l,mid,d^1);
u->r=build(mid+1,r,d^1);
u->up();
return u;
}
void pia(node *u){ // private
if(u==Null)return;
pia(u->l);
cache.push_back(u);
pia(u->r);
}
void insert(node *&u,int x,int y,int v){ // private
if(u==Null){
*++pl=node(x,y,v); u=pl; u->d=0;
return;
}
insert(u->ch(x,y),x,y,v);
u->up();
if(0.725*u->sz <= max(u->l->sz,u->r->sz)){
cache.clear();
pia(u);
u=build(cache.data(),cache.data()+cache.size(),u->d);
}
}
void insert(int x,int y,int v){
insert(rt,x,y,v);
}
range qx,qy;
int query(node *u){ // private
if(u==Null)return U0;
if(u->xx.out(qx) || u->yy.out(qy))return U0;
if(u->xx.in(qx) && u->yy.in(qy))return u->s;
return U((range{u->x,u->x}.in(qx) && range{u->y,u->y}.in(qy) ? u->a : U0),
U(query(u->l),query(u->r)));
}
int query(int x1,int y1,int x2,int y2){
qx={x1,x2};
qy={y1,y2};
return query(rt);
}
}tr;
kdt::node kdt::Null[N],*kdt::pl;
- 模板题,支持询问近邻。
struct kdt{
struct range{
lf l,r;
range operator|(range b)const{
return {min(l,b.l),max(r,b.r)};
}
lf dist(lf x){
return x<l ? l-x : x>r ? x-r : 0;
}
};
struct node{
lf x,y; // (x,y): coordinate
int d,sz; // d: cut direction, sz: size of subtree
range xx,yy; // xx/yy: range of coordinate x/y
node *l,*r; // left/right child
lf dist(lf x,lf y){
return hypot(xx.dist(x),yy.dist(y));
}
void up(){
sz=l->sz+r->sz+1;
xx=range{x,x} | l->xx | r->xx;
yy=range{y,y} | l->yy | r->yy;
}
node(){
sz=0;
xx=yy={inf,-inf};
l=r=Null;
}
node(lf x_,lf y_){
x=x_,y=y_;
l=r=Null;
up();
}
}*rt;
static node Null[N],*pl;
vector<node *> cache;
void init(){ // while using kdtrees, notice Null is static
rt=pl=Null;
}
node *build(node **l,node **r,int d){ // private
if(l>=r)return Null;
node **mid=l+(r-l)/2;
if(d==0)
nth_element(l,mid,r,[&](node *a,node *b){
return a->x < b->x;
});
else
nth_element(l,mid,r,[&](node *a,node *b){
return a->y < b->y;
});
node *u=*mid;
u->d=d;
u->l=build(l,mid,d^1);
u->r=build(mid+1,r,d^1);
u->up();
return u;
}
void build(pair<lf,lf> a[],int n){
init(); cache.clear();
repeat(i,0,n){
*++pl=node(a[i].fi,a[i].se);
cache.push_back(pl);
}
rt=build(cache.data(),cache.data()+cache.size(),0);
}
lf ans;
void mindis(node *u,lf x,lf y,node *s){ // private
if(u==Null)return;
if(u!=s)ans=min(ans,hypot(x-u->x,y-u->y));
lf d1=u->l->dist(x,y);
lf d2=u->r->dist(x,y);
if(d1<d2){ // optimize
if(ans>d1)mindis(u->l,x,y,s);
if(ans>d2)mindis(u->r,x,y,s);
}
else{
if(ans>d2)mindis(u->r,x,y,s);
if(ans>d1)mindis(u->l,x,y,s);
}
}
lf mindis(lf x,lf y,node *s){ // min distance from (x,y), while delete node s
ans=inf;
mindis(rt,x,y,s);
return ans;
}
}tr;
kdt::node kdt::Null[N],*kdt::pl;
- 删除操作。
struct kdt{
struct range{
int l,r;
range operator|(range b){
return {min(l,b.l),max(r,b.r)};
}
int dist(int x){
return x>0 ? r*x : l*x;
}
};
struct node{
int x,y; // (x,y): coordinate
int d,sz,exsz,exist; // d: cut direction, sz: size of subtree
range xx,yy; // xx/yy: range of coordinate x/y
node *l,*r; // left/right child
int dist(int x,int y){
return xx.dist(x)+yy.dist(y);
}
node *&ch(int px,int py){ // in which child
if(d==0)return make_pair(px,py)<make_pair(x,y) ? l : r;
else return make_pair(py,px)<make_pair(y,x) ? l : r;
}
void up(){
exsz=l->exsz+r->exsz+!!exist;
sz=l->sz+r->sz+1;
xx=range{x,x} | l->xx | r->xx;
yy=range{y,y} | l->yy | r->yy;
}
node(){
exist=exsz=sz=0;
xx=yy={inf,-inf};
l=r=Null;
}
node(int x_,int y_){
exist=1;
x=x_,y=y_;
l=r=Null;
up();
}
}*rt;
static node Null[N],*pl;
vector<node *> cache;
void init(){ // while using kdtrees, notice Null is static
rt=pl=Null;
}
node *build(node **l,node **r,int d){ // private
if(l>=r)return Null;
node **mid=l+(r-l)/2;
if(d==0)
nth_element(l,mid,r,[&](node *a,node *b){
return make_pair(a->x,a->y) < make_pair(b->x,b->y);
});
else
nth_element(l,mid,r,[&](node *a,node *b){
return make_pair(a->y,a->x) < make_pair(b->y,b->x);
});
node *u=*mid;
u->d=d;
u->l=build(l,mid,d^1);
u->r=build(mid+1,r,d^1);
u->up();
return u;
}
void pia(node *u){ // private
if(u==Null)return;
pia(u->l);
if(u->exist)cache.push_back(u);
pia(u->r);
}
void insert(node *&u,int x,int y){ // private
if(u==Null){
*++pl=node(x,y); u=pl; u->d=0;
return;
}
if(u->x==x && u->y==y){
u->exist++;
return;
}
insert(u->ch(x,y),x,y);
u->up();
if(0.9*u->sz <= max(u->l->sz,u->r->sz) || 0.5*u->sz >= u->exsz){
cache.clear();
pia(u);
u=build(cache.data(),cache.data()+cache.size(),u->d);
}
}
void insert(int x,int y){
insert(rt,x,y);
}
void erase(node *&u,int x,int y){
if(u->x==x && u->y==y){
u->exist--;
}
else{
erase(u->ch(x,y),x,y);
}
u->up();
if(0.9*u->sz <= max(u->l->sz,u->r->sz) || 0.5*u->sz >= u->exsz){
cache.clear();
pia(u);
u=build(cache.data(),cache.data()+cache.size(),u->d);
}
}
void erase(int x,int y){
erase(rt,x,y);
}
int ans;
void mindis(node *u,int x,int y){ // private
if(u==Null)return;
if(u->exist)ans=max(ans,x*u->x+y*u->y);
int d1=u->l->dist(x,y);
int d2=u->r->dist(x,y);
if(d1>d2){ // optimize
if(ans<d1 && u->l->sz)mindis(u->l,x,y);
if(ans<d2 && u->r->sz)mindis(u->r,x,y);
}
else{
if(ans<d2 && u->r->sz)mindis(u->r,x,y);
if(ans<d1 && u->l->sz)mindis(u->l,x,y);
}
}
int mindis(int x,int y){ // min distance from (x,y), while delete node s
ans=-INF;
mindis(rt,x,y);
return ans;
}
void dfs(node *u){
if(u==Null)return;
printf("(%lld,%lld)[",u->x,u->y);
dfs(u->l); printf(",");
dfs(u->r); printf("]");
}
void print(){
dfs(rt);
puts("");
}
}tr;
kdt::node kdt::Null[N],*kdt::pl;
Splay
- 均摊 \(O(\log n)\)
struct Splay{
struct node{
node *ch[2],*fa;
int v,sz;
void up(){sz=ch[0]->sz+ch[1]->sz+1;}
node(int _v){v=_v; ch[0]=ch[1]=fa=Null; up();}
node(){ch[0]=ch[1]=fa=Null; sz=0;}
bool cmp(int key){return v<key;}
}*rt;
bool get(node *u){return u==u->fa->ch[1];}
static node Null[N],*pl;
void init(){
rt=pl=Null;
}
void rotate(node *r){ // private
node *u=r->fa;
bool d=get(r);
if(r->ch[d^1]!=Null)r->ch[d^1]->fa=u;
if(u->fa!=Null)u->fa->ch[get(u)]=r;
u->ch[d]=r->ch[d^1];
r->ch[d^1]=u;
r->fa=u->fa; u->fa=r;
u->up(); r->up();
}
node *splay(node *u){ // private
for(node *f=u->fa;f!=Null;rotate(u),f=u->fa)
if(f->fa!=Null)rotate(get(u)==get(f)?f:u);
return rt=u;
}
void insert(int v){
node *u=rt,*f=Null;
while(u!=Null){
f=u; u=u->ch[u->cmp(v)];
}
*++pl=node(v); pl->fa=f;
if(f!=Null)f->ch[f->cmp(v)]=pl;
splay(pl);
}
node *merge(node *u,node *r){ // private
if(u==Null)return r;
if(r==Null)return u;
while(u->ch[1]!=Null)u=u->ch[1];
splay(u);
u->ch[1]=r; r->fa=u; u->up();
return u;
}
node *find(node *u,int key){ // private
if(u->v==key)return u;
return find(u->ch[u->cmp(key)],key);
}
void erase(int key){
splay(find(rt,key));
node *u=rt->ch[0],*v=rt->ch[1];
u->fa=Null; v->fa=Null;
rt=merge(u,v);
}
int order(int key){
int ans=0; node *u=rt,*f=Null;
while(u!=Null){
int d=u->cmp(key);
if(d)ans+=u->ch[0]->sz+1;
f=u; u=u->ch[d];
}
splay(f);
return ans;
}
int kth(int k){
node *u=rt;
while(1){
int s=u->ch[0]->sz;
if(k==s){splay(u); return u->v;}
if(k>s)k-=s+1,u=u->ch[1];
else u=u->ch[0];
}
}
}tr;
Splay::node Splay::Null[N],*Splay::pl;
// tr.insert(x); // 插入 x
// tr.erase(x); // 删除单个 x
// printf("%d\n",tr.order(x)+1); // x 的排名
// printf("%d\n",tr.kth(x-1)); // 排名为 x
// printf("%d\n",tr.kth(tr.order(x)-1)); // x 的前驱
// printf("%d\n",tr.kth(tr.order(x+1))); // x 的后继
文艺
struct Splay{
struct node{
node *ch[2],*fa;
int v,sz,tag;
void up(){sz=ch[0]->sz+ch[1]->sz+1;}
node(int _v){v=_v; ch[0]=ch[1]=fa=Null; tag=0; up();}
node(){ch[0]=ch[1]=fa=Null; sz=0;}
bool cmp(int key){return v<key;}
void down(){
if(tag){
swap(ch[0],ch[1]);
ch[0]->tag^=1;
ch[1]->tag^=1;
tag=0;
}
}
}*rt;
bool get(node *u){return u==u->fa->ch[1];}
static node Null[N],*pl;
void init(){
rt=pl=Null;
}
void rotate(node *r){ // private
node *u=r->fa;
bool d=get(r);
if(r->ch[d^1]!=Null)r->ch[d^1]->fa=u;
if(u->fa!=Null)u->fa->ch[get(u)]=r;
u->ch[d]=r->ch[d^1];
r->ch[d^1]=u;
r->fa=u->fa; u->fa=r;
u->up(); r->up();
}
node *splay(node *u){ // private
for(node *f=u->fa;f!=Null;rotate(u),f=u->fa)
if(f->fa!=Null)rotate(get(u)==get(f)?f:u);
return rt=u;
}
template<typename ptr>
void build(ptr l,ptr r){
init(); sort(l,r);
for(auto i=l;i!=r;i++){
*++pl=node(*i);
if(rt!=Null)rt->fa=pl;
pl->ch[0]=rt;
rt=pl;
rt->up();
}
}
node *kth(node *u,int k){
while(1){
u->down();
int s=u->ch[0]->sz;
if(k==s){splay(u); return u;}
if(k>s)k-=s+1,u=u->ch[1];
else u=u->ch[0];
}
}
node *merge(node *u,node *r){ // private
if(u==Null)return r;
if(r==Null)return u;
while(u->down(),u->ch[1]!=Null)u=u->ch[1];
splay(u);
u->ch[1]=r; r->fa=u; u->up();
return u;
}
pair<node *,node *> split(node *u,int k){ // private
if(k==-1)return {Null,u};
u=kth(u,k);
node *v=u->ch[1];
if(v!=Null){u->ch[1]=Null; v->fa=Null; u->up();}
return {u,v};
}
void add_tag(int l,int r){
node *a,*b,*c;
tie(a,b)=split(rt,l-1);
tie(b,c)=split(b,r-l);
b->tag^=1;
rt=merge(a,merge(b,c));
}
void output(node *u){
if(u==Null)return; u->down();
output(u->ch[0]); cout<<u->v<<' '; output(u->ch[1]);
}
void print(){
output(rt); cout<<endl;
}
}tr;
Splay::node Splay::Null[N],*Splay::pl;
动态森林 using LCT
- \(O(\log n)\) 但是常数很大。
#define lc ch[0]
#define rc ch[1]
struct lct{
struct node{
node *ch[2],*fa;
int v,s; bool rev;
node(){lc=rc=fa=Null; v=s=0;}
node(int _v){lc=rc=fa=Null; v=_v; up();}
void up(){s=lc->s ^ rc->s ^ v;}
bool nroot(){
return fa->lc==this || fa->rc==this;
}
void down(){
if(rev){
swap(lc,rc);
lc->rev^=1;
rc->rev^=1;
rev=0;
}
}
};
static node Null[N],*pl;
void init(){
pl=Null;
}
void rotate(node *u){
node *f=u->fa,*z=f->fa;
bool k=(f->rc==u);
node *w=u->ch[!k];
if(f->nroot())z->ch[z->rc==f]=u;
u->ch[!k]=f; f->ch[k]=w;
if(w!=Null)w->fa=f;
f->fa=u; u->fa=z;
f->up();
}
void downchain(node *u){
if(u->nroot())downchain(u->fa);
u->down();
}
void splay(node *u){
downchain(u);
while(u->nroot()){
node *f=u->fa,*z=f->fa;
if(f->nroot())
rotate((f->lc==u)!=(z->lc==f)?u:f);
rotate(u);
}
u->up();
}
void access(node *u){
for(node *c=Null;u!=Null;c=u,u=u->fa)
splay(u),u->rc=c,u->up();
}
void makeroot(node *u){
access(u);
splay(u);
u->rev^=1;
}
node *findroot(node *u){
access(u);
splay(u);
while(u->lc!=Null)u->down(),u=u->lc;
splay(u);
return u;
}
void split(node *x,node *y){
makeroot(x);
access(y);
splay(y);
}
void link(node *x,node *y){
makeroot(x);
if(findroot(y)!=x)x->fa=y;
}
void cut(node *x,node *y){
makeroot(x);
if(findroot(y)==x && y->fa==x && y->lc==Null){
y->fa=x->rc=Null;
x->up();
}
}
}tr;
lct::node lct::Null[N],*lct::pl;
lct::node *a[N];
void Solve(){
int n=read(),m=read();
tr.init();
repeat(i,1,n+1){
*++lct::pl=lct::node(read());
a[i]=lct::pl;
}
while(m--){
int op=read(),x=read(),y=read();
if(op==0){
tr.split(a[x],a[y]); // 将 x 到 y 的路径剖出来
print(a[y]->s,1);
}
if(op==1){
tr.link(a[x],a[y]); // 连接 x, y
}
if(op==2){
tr.cut(a[x],a[y]); // 删除 (x, y) 边
}
if(op==3){
tr.splay(a[x]);
a[x]->v=y;
}
}
}
莫队
- 离线(甚至在线)处理区间问题,
猛得一批
普通莫队
- 移动指针 l, r 来求所有区间的答案
- 块大小为 \(\sqrt n\),\(O(n^{\tfrac 3 2})\)
int unit,n,bkt[N],a[N],final[N]; // bkt是桶
ll ans;
struct node{
int l,r,id;
bool operator<(const node &b)const{
if(l/unit!=b.l/unit)return l<b.l; // 按块排序
if((l/unit)&1) // 奇偶化排序
return r<b.r;
return r>b.r;
}
};
vector<node> query; // 查询区间
void update(int x,int d){
int &b=bkt[a[x]];
ans-=C(b,2); // 操作示例
b+=d;
ans+=C(b,2); // 操作示例
}
void solve(){ // final[]即最终答案
fill(bkt,bkt+n+1,0);
unit=sqrt(n)+1;
sort(query.begin(),query.end());
int l=1,r=0; ans=0; // 如果原数组a编号从1开始
for(auto i:query){
while(l<i.l)update(l++,-1);
while(l>i.l)update(--l,1);
while(r<i.r)update(++r,1);
while(r>i.r)update(r--,-1);
final[i.id]=ans;
}
}
// repeat(i,0,m)query.push_back({read(),read(),i}); // 输入查询区间
带修莫队
- 相比与普通莫队,多了一个时间轴。
- 块大小为 \(\sqrt[3]{nt}\),\(O(\sqrt[3]{n^4t})\)。
int unit,n,bkt[1000010],a[N],final[N];
ll ans;
struct node{
int l,r,t,id;
bool operator<(const node &b)const{
if(l/unit!=b.l/unit)return l<b.l;
if(r/unit!=b.r/unit)return r<b.r;
return r/unit%2?t<b.t:t>b.t;
}
};
struct node2{int x,pre,nxt;};
vector<node> query;
vector<node2> change;
void update(int x,int d){
int &b=bkt[a[x]];
ans-=!!b;
b+=d;
ans+=!!b;
}
void solve(){
unit=pow(1.0*n*change.size(),1.0/3)+1;
sort(query.begin(),query.end());
int l=1,r=0,t=change.size(); ans=0;
for(auto i:query){
while(l<i.l)update(l++,-1);
while(l>i.l)update(--l,1);
while(r<i.r)update(++r,1);
while(r>i.r)update(r--,-1);
while(t<i.t){
int f=(change[t].x>=i.l && change[t].x<=i.r);
if(f)update(change[t].x,-1);
a[change[t].x]=change[t].nxt;
if(f)update(change[t].x,1);
t++;
}
while(t>i.t){
t--;
int f=(change[t].x>=i.l && change[t].x<=i.r);
if(f)update(change[t].x,-1);
a[change[t].x]=change[t].pre;
if(f)update(change[t].x,1);
}
final[i.id]=ans;
}
}
void Solve(){
n=read(); int q=read();
repeat(i,1,n+1)a[i]=read();
while(q--){
static char s[10]; scanf("%s",s);
if(*s=='Q'){
int l=read(),r=read();
query.push_back((node){l,r,(int)change.size(),(int)query.size()});
}
else{
int x=read(),y=read();
change.push_back((node2){x,a[x],y});
a[x]=y;
}
}
solve();
repeat(i,0,query.size())
printf("%d\n",final[i]);
}
回滚莫队
- 解决区间只能扩张不能收缩的问题。
- 对于块内区间,直接暴力;对于所有左端点在一个块的区间,从块的右端点出发,先扩张右边界再扩张左边界再回滚左边界。
- \(O(n^{\tfrac 3 2})\)。
int n,unit;
ll a[N],ans[N];
typedef array<int,3> node; // l,r,id
vector<node> q;
struct bucket{
ll a[N],vis[N],dcnt; ll ans=0;
vector<pair<ll *,ll>> rec;
void init(){dcnt++; ans=0; rec.clear();}
void push(ll x,int flag){
if(vis[x]!=dcnt)vis[x]=dcnt,a[x]=0;
if(flag){
rec.push_back({&a[x],a[x]});
rec.push_back({&ans,ans});
}
a[x]++;
ans=max(ans,x*a[x]);
}
void rollback(){
repeat_back(i,0,rec.size())*rec[i].fi=rec[i].se;
rec.clear();
}
}bkt;
void Solve(){
n=read(); int Q=read(); unit=sqrt(n)+1;
repeat(i,0,n)a[i]=read();
repeat(i,0,Q){
int x=read()-1,y=read()-1;
if(x/unit==y/unit){
bkt.init();
repeat(k,x,y+1)bkt.push(a[k],0);
ans[i]=bkt.ans;
}
else q.push_back({x,y,i});
}
sort(q.begin(),q.end(),[](node a,node b){
return pii(a[0]/unit,a[1])<pii(b[0]/unit,b[1]);
});
int p=0;
for(int i=unit;i<n;i+=unit){
bkt.init(); int r=i-1;
while(p!=(int)q.size() && q[p][0]<i){
while(r<q[p][1])bkt.push(a[++r],0);
repeat(j,q[p][0],i)bkt.push(a[j],1);
ans[q[p][2]]=bkt.ans;
bkt.rollback();
p++;
}
}
repeat(i,0,Q)printf("%lld\n",ans[i]);
}
冷门数据结构
珂朵莉树 / 老司机树
- 珂朵莉数以区间形式存储数据,
非常暴力,适用于有区间赋值操作且数据随机的题。 - 对于随机数据有均摊 \(O(n\log\log n)\),不随机数据可能被卡。
struct ODT{
struct node{
int l,r;
mutable int v; // 强制可修改
bool operator<(const node &b)const{return l<b.l;}
};
set<node> a;
void init(){
a.clear();
a.insert({-inf,inf,0});
}
set<node>::iterator split(int x){ // 分裂区间
auto it=--a.upper_bound({x,0,0});
if(it->l==x)return it;
int l=it->l,r=it->r,v=it->v;
a.erase(it);
a.insert({l,x-1,v});
return a.insert({x,r,v}).first;
}
void assign(int l,int r,int v){ // 区间赋值
auto y=split(r+1),x=split(l);
a.erase(x,y);
a.insert({l,r,v});
}
int sum(int l,int r){ // 操作示例:区间求和
auto y=split(r+1),x=split(l);
int ans=0;
for(auto i=x;i!=y;i++){
ans+=(i->r-i->l+1)*i->v;
}
return ans;
}
}odt;
划分树
- 静态区间第 k 小,可代替主席树
- 编号从 1 开始,初始化 \(O(n\log n)\),查询 \(O(\log n)\)
struct divtree{
int a[N],pos[25][N],tr[25][N],n;
void build(int l,int r,int dep){ // private
if(l==r)return;
int m=(l+r)>>1;
int same=m-l+1;
repeat(i,l,r+1)
same-=(tr[dep][i]<a[m]);
int ls=l,rs=m+1;
repeat(i,l,r+1){
int flag=0;
if(tr[dep][i]<a[m] || (tr[dep][i]==a[m] && same>0)){
flag=1;
tr[dep+1][ls++]=tr[dep][i];
same-=(tr[dep][i]==a[m]);
}
else{
tr[dep+1][rs++]=tr[dep][i];
}
pos[dep][i]=pos[dep][i-1]+flag;
}
build(l,m,dep+1);
build(m+1,r,dep+1);
}
int query(int ql,int qr,int k,int L,int R,int dep=0){ // private
if(ql==qr)
return tr[dep][ql];
int m=(L+R)>>1;
int x=pos[dep][ql-1]-pos[dep][L-1];
int y=pos[dep][qr]-pos[dep][L-1];
int rx=ql-L-x,ry=qr-L-y;
int cnt=y-x;
if(cnt>=k)
return query(L+x,L+y-1,k,L,m,dep+1);
else
return query(m+rx+1,m+1+ry,k-cnt,m+1,R,dep+1);
}
int mink(int l,int r,int k){ // k>=1, k<=r-l+1
return query(l,r,k,1,n);
}
int maxk(int l,int r,int k){ // k>=1, k<=r-l+1
return query(l,r,r-l+2-k,1,n);
}
void init(int _n){
n=_n;
repeat(i,1,n+1)tr[0][i]=a[i]=in[i];
sort(a+1,a+n+1);
build(1,n,0);
}
}tr;
析合树
- 定义连续段为一个区间,区间内所有数排序后为连续正整数
- 析合树:每个节点对应一个连续段,合点由儿子顺序或倒序组成,析点为乱序
- 给定一个排列,询问包含给定区间的最短连续段
- 注意代码里N已经是两倍大小了,编号从 1 开始,\(O(n\log n)\)
int n, m, a[N], st1[N], st2[N], tp1, tp2, rt;
int L[N], R[N], M[N], id[N], cnt, typ[N], st[N], tp;
#define log(x) (31 - __builtin_clz(x))
struct RMQ {
int mn[N][17], mx[N][17];
void build() {
repeat(i, 1, n + 1) mn[i][0] = mx[i][0] = a[i];
repeat(i, 1, 17) repeat(j, 1, n - (1 << i) + 2) {
mn[j][i] = min(mn[j][i - 1], mn[j + (1 << (i - 1))][i - 1]);
mx[j][i] = max(mx[j][i - 1], mx[j + (1 << (i - 1))][i - 1]);
}
}
int qmin(int l, int r) {
int t = log(r - l + 1);
return min(mn[l][t], mn[r - (1 << t) + 1][t]);
}
int qmax(int l, int r) {
int t = log(r - l + 1);
return max(mx[l][t], mx[r - (1 << t) + 1][t]);
}
} D;
#define ls (k << 1)
#define rs (k << 1 | 1)
struct SEG {
int a[N << 1], z[N << 1];
void up(int k) { a[k] = min(a[ls], a[rs]); }
void mfy(int k, int v) { a[k] += v, z[k] += v; }
void down(int k) {
if (z[k]) mfy(ls, z[k]), mfy(rs, z[k]), z[k] = 0;
}
void update(int k, int l, int r, int x, int y, int v) {
if (x > r || y < l) return;
if (x<=l && r<=y) {
mfy(k, v);
return;
}
down(k);
int mid = (l + r) >> 1;
update(ls, l, mid, x, y, v);
update(rs, mid + 1, r, x, y, v);
up(k);
}
int query(int k, int l, int r) {
if (l == r) return l;
down(k);
int mid = (l + r) >> 1;
return a[ls] == 0 ? query(ls, l, mid) : query(rs, mid + 1, r);
}
} T;
int dep[N], fa[N][18];
vector<int> e[N];
void add(int u, int v) { e[u].push_back(v); }
void dfs(int u) {
repeat(i, 1, log(dep[u]) + 1) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (auto v : e[u]) {
dep[v] = dep[u] + 1;
fa[v][0] = u;
dfs(v);
}
}
int go(int u, int d) {
for (int i = 0; i < 18 && d; ++i)
if (d & (1 << i)) d ^= 1 << i, u = fa[u][i];
return u;
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
u = go(u, dep[u] - dep[v]);
if (u == v) return u;
for (int i = 17; ~i; --i)
if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
bool judge(int l, int r) { return D.qmax(l, r) - D.qmin(l, r) == r - l; }
void build() {
repeat(i, 1, n + 1) {
while (tp1 && a[i] <= a[st1[tp1]])
T.update(1, 1, n, st1[tp1 - 1] + 1, st1[tp1], a[st1[tp1]]), tp1--;
while (tp2 && a[i] >= a[st2[tp2]])
T.update(1, 1, n, st2[tp2 - 1] + 1, st2[tp2], -a[st2[tp2]]), tp2--;
T.update(1, 1, n, st1[tp1] + 1, i, -a[i]);
st1[++tp1] = i;
T.update(1, 1, n, st2[tp2] + 1, i, a[i]);
st2[++tp2] = i;
id[i] = ++cnt;
L[cnt] = R[cnt] = i;
int le = T.query(1, 1, n), now = cnt;
while (tp && L[st[tp]] >= le) {
if (typ[st[tp]] && judge(M[st[tp]], i)) {
R[st[tp]] = i, add(st[tp], now), now = st[tp--];
} else if (judge(L[st[tp]], i)) {
typ[++cnt] = 1;
L[cnt] = L[st[tp]], R[cnt] = i, M[cnt] = L[now];
add(cnt, st[tp--]), add(cnt, now);
now = cnt;
} else {
add(++cnt, now);
do {
add(cnt, st[tp--]);
} while (tp && !judge(L[st[tp]], i));
L[cnt] = L[st[tp]], R[cnt] = i, add(cnt, st[tp--]);
now = cnt;
}
}
st[++tp] = now;
T.update(1, 1, n, 1, i, -1);
}
rt = st[1];
}
void query(int &l, int &r) {
int x = id[l], y = id[r];
int z = lca(x, y);
if (typ[z] & 1)
l = L[go(x, dep[x] - dep[z] - 1)], r = R[go(y, dep[y] - dep[z] - 1)];
else
l = L[z], r = R[z];
}
int main() {
scanf("%d", &n);
repeat(i, 1, n + 1) scanf("%d", &a[i]);
D.build();
build();
dfs(rt);
scanf("%d", &m);
while (m--) {
int x, y;
scanf("%d%d", &x, &y);
query(x, y);
printf("%d %d\n", x, y);
}
return 0;
}
造轮子
struct of 二维数组
- (可以存储类似 \(n\times m\le 2\times 10^5\) 的二维数组)
struct mat{
ll a[N]; int n,m;
void operator()(int _n,int _m){n=_n,m=_m;} // initialization
ll *operator[](int x){
return a+x*m;
}
/*
void print(){
repeat(i,0,n)
repeat(j,0,m)
printf("%3lld%c",a[i*m+j]," \n"[j==m-1]);
}
friend mat T(mat &a){
mat b; b(a.m,a.n);
repeat(i,0,b.n)
repeat(j,0,b.m)
b[i][j]=a[j][i];
return b;
}
*/
}a;
Hash 表
- 该 Hash 表耗时约为 unordered_map 的 40%。
- uN 取值表:
1009, 2003, 3001, 5003,
10007, 20011, 30011, 50021,
100003, 200003, 300007, 500009,
1000003, 2000003, 3000017, 5000011,
10000019, 20000003, 30000023
template<typename A,typename B>
struct unmap{
struct node{
A u; B v; int nxt;
};
static const unsigned uN=20000003;
vector<node> e;
int head[uN];
unmap(){clear();}
void clear(){mst(head,-1); e.clear();}
bool count(A u){
int h=u%uN;
for(int i=head[h];~i;i=e[i].nxt)
if(e[i].u==u)return 1;
return 0;
}
B &operator[](A u){
int h=u%uN;
for(int i=head[h];~i;i=e[i].nxt)
if(e[i].u==u)return e[i].v;
e.push_back({u,B(),head[h]}); head[h]=e.size()-1;
return e.back().v;
}
};