高级数据结构分块莫队笛卡尔树算法模板几题

本文介绍了如何使用分块和莫队数据结构优化区间加法查询,并结合笛卡尔树实现区间内的前驱搜索。通过预先对块进行排序和维护懒惰更新,达到O(q*sqrt(N)log(sqrt(N)))的时间复杂度。讨论了区间mex问题的分块解决方案,以及异或查询和频繁值问题的处理技巧。
摘要由CSDN通过智能技术生成

ahhhh留下点会议行不行


分块

模板
对区间加上k,求出某个区间的和

using namespace std;
#define int long long
const int N = 1e6+10;
int n, m, q;
int a[N], b[N];
int l[N], r[N];//存块的左右端点
int belong[N], add[N], sum[N];//belong表示属于哪个块
int B;
void update(int x, int y, int c) {
	if(belong[x]==belong[y]) {//在同一块里暴力更新
		for(int i=x; i<=y; i++) a[i]+=c;
		sum[belong[x]]+=(y-x+1)*c;
		return ;
	}
	for(int i=belong[x]+1; i<=belong[y]-1; i++) add[i]+=c;//好多块来个lazy标记
	for(int i=x; i<=r[belong[x]]; i++) a[i]+=c;//在同一块里暴力更新
	sum[belong[x]]+=(r[belong[x]]-x+1)*c;
	for(int i=l[belong[y]]; i<=y; i++) a[i]+=c;//在同一块里暴力更新
	sum[belong[y]]+=(y-l[belong[x]]+1)*c;
}
int ask(int x, int y) {
	int ans=0;
	if(belong[x]==belong[y]) {
		for(int i=x; i<=y; i++) ans+=a[i]+add[belong[x]];

		return ans;
	}
	for(int i=belong[x]+1; i<=belong[y]-1;i++) {
		ans+=sum[i]+add[i]*B;
    }
	for(int i=x; i<=r[belong[x]]; i++) ans+=a[i]+add[belong[x]];
	for(int i=l[belong[y]]; i<=y; i++) ans+=a[i]+add[belong[y]];
    return ans;
}
signed main() {
    cin>>n>>q;
    for(int i=1; i<=n; i++) cin>>a[i];
    B=sqrt(n);
	int num=(n+B-1)/B;
	for(int i=1; i<=n; i++) belong[i]=(i+B-1)/B, sum[belong[i]]+=a[i];
	for(int i=1; i<=num; i++) l[i]=(i-1)*B+1, r[i]=i*B;
	for(int i=1; i<=num; i++) sort(b+l[i], b+r[i]+1);
    int c, x, y, w;
    for(int i=1;i<=q;i++) {
        cin>>c>>x>>y;
        if(c==1) cin>>w, update(x, y, w);
        else cout<<ask(x, y)<<endl;
    }
    return 0;
}

给出一个长为 的数列,以及 个操作,操作涉及区间加法,询问区间内小于某个值 的前驱(比其小的最大元素)。
问块里小于c的想到二分
对于零散块的更新也要这个零散块的整个块进行sort
复杂度 O ( q ∗ s q r t ( N ) l o g ( s q r t ( N ) ) ) O(q*sqrt(N)log(sqrt(N))) O(qsqrt(N)log(sqrt(N)))

using namespace std;
#define int long long
const int N = 1e6+10;
int n, q, a[N], b[N], l[N], r[N], belong[N], add[N], B;
void reset(int x) {
	for(int i=l[belong[x]]; i<=r[belong[x]]; i++) b[i]=a[i];
	sort(b+l[belong[x]], b+r[belong[x]]+1);
}
void update(int x, int y, int c) {
	if(belong[x]==belong[y]) {
		for(int i=x; i<=y; i++) a[i]+=c;
		reset(x);
		return ;
	}
	for(int i=x; i<=r[belong[x]]; i++) a[i]+=c;
	for(int i=l[belong[y]]; i<=y; i++) a[i]+=c;
	reset(x);
	reset(y);
	for(int i=belong[x]+1; i<=belong[y]-1; i++) add[i]+=c;
}
int ask(int x, int y, int c) {
	int ans=-2e18;
	if(belong[x]==belong[y]) {
		for(int i=x; i<=y; i++) {
			if(a[i]+add[belong[x]]<c) ans=max(ans, a[i]+add[belong[x]]);
		}
		if(ans==-2e18) ans=-1;
		return ans;
	}
	for(int i=x; i<=r[belong[x]]; i++) if(a[i]+add[belong[x]]<c) ans=max(ans, a[i]+add[belong[x]]);
	for(int i=l[belong[y]]; i<=y; i++) if(a[i]+add[belong[y]]<c) ans=max(ans, a[i]+add[belong[y]]);
	for(int i=belong[x]+1; i<=belong[y]-1;i++) {
		int qq=lower_bound(b+l[i], b+r[i]+1, c-add[i])-b-1;//二分查找的是c-add[i]
		if(b[qq]<c-add[i]) ans=max(ans, b[qq]+add[i]);//这里调了好久 最后的ans要和b[qq]+add[i]比较不是b[i]//有可能是找不到就返回首位置就要if判断一下
    }
    if(ans==-2e18) ans=-1;
    return ans;
}

莫队

P4137 Rmq Problem / mex莫队+分块
求区间mex直接分块
询问通过分块sort一下然后莫队跑

using namespace std;
const int N = 2e5 + 5;
const int B = sqrt(N);
int a[N], cnt[N], n, m, tans, ans[N], zhengge[N];//记整个块内有的数的数量
int l=1, r=0;//一直t一个点把l=1,r=0;从主函数搬到外面就不t了。。。。快了0.03s
struct Q {
    int l, r, id;
	bool operator<(const Q &t) const {
        return l/B==t.l/B ? r<t.r: l/B<t.l/B;
    }
}q[N];
int get(int x) {return x/B;}
void update(int x, int f) {
	cnt[x]+=f;
    if(cnt[x]==0&&f==-1) zhengge[get(x)]+=f;
    else if(cnt[x]==1&&f==1) zhengge[get(x)]+=f;
}
int main() {
    n=read(); m=read();
    for(int i=1; i<=n; i++) a[i]=read();
    for(int i=1; i<=m; i++) {
		q[i].l=read();q[i].r=read();
		q[i].id=i;
    }
    sort(q+1, q+m+1);
    for(int i=1; i<=m; i++) {
        while(l<q[i].l) update(a[l++], -1);
        while(l>q[i].l) update(a[--l], 1);
        while(r<q[i].r) update(a[++r], 1);
        while(r>q[i].r) update(a[r--], -1);
		int qq=0;
		for(int k=0; k<=B; k++) {
			if(zhengge[k]<B) {
				for(int j=k*B; j<=k*B+B-1; j++) {
					if(cnt[j]==0) {
						ans[q[i].id]=j;
						qq=1;
						break;
					}
				}
			}
			if(qq) break;
		}
    }
    for(int i=1; i<=m; i++) {
		cout<<ans[i]<<"\n";
    }
    return 0;
}

XOR and Favorite Numbercf2200
求区间段异或所以想到存区间前缀和
假设现在位置为 R R R,异或前缀和为 x x x,我们需要多少个左区间满足 x x x^ p r e [ L − 1 ] = = k pre[L-1]==k pre[L1]==k 等价于有多少个 p r e [ L − 1 ] = = x pre[L-1]==x pre[L1]==x ^ k k k,用一个桶去装
但是由于区间 [ L , R ] [L, R] [L,R]的异或值为 p r e [ R ] pre[R] pre[R] ^ p r e [ L − 1 ] pre[L-1] pre[L1]所以需要把左端点移动一下

#define int long long//tans和ans[]都会爆int 找学长找了bug
using namespace std;
const int N = 2e6 + 5;
int a[N], cnt[N], n, m, k, tans, ans[N], B;
struct Q {
    int l, r, id;
	bool operator<(const Q &t) const {
        return l/B==t.l/B ? r<t.r: l/B<t.l/B;
    }
}q[N];
void update(int x, int f) {
	if(f==1) tans+=cnt[x^k], cnt[x]++;
	else cnt[x]--, tans-=cnt[x^k];
}
signed main() {
    n=read();m=read();k=read();
    B=sqrt(1.0*n);
    for(int i=1; i<=n; i++) a[i]=read(), a[i]=a[i-1]^a[i];//前缀异或值
    for(int i=1; i<=m; i++) {
		q[i].l=read();q[i].r=read();
		q[i].l--;//主要因为是pre[L-1]//不这样写的话要cnt[0]并且下面的第一个update的a[l++]变为a[l-1]再l++第二个要变为
		q[i].id=i;
    }
    sort(q+1, q+m+1);
    int l=1, r=0;
    for(int i=1; i<=m; i++) {
        while(l<q[i].l) update(a[l++], -1);
        while(l>q[i].l) update(a[--l], 1);
        while(r<q[i].r) update(a[++r], 1);
        while(r>q[i].r) update(a[r--], -1);
		ans[q[i].id]=tans;
    }
    for(int i=1; i<=m; i++) {
		cout<<ans[i]<<"\n";
    }
    return 0;
}

D. Cut and Stickcf2000(没补
P7416 [USACO21FEB] No Time to Dry P个人赛5(没补
Frequent values 可能莫队(

带修改的莫队
给一个长为n的序列m个操作
1 l r 1 l r 1lr [ l , r ] [l, r] [l,r]内不相同的个数
2 p x 2 p x 2px a [ p ] a[p] a[p]修改为 x x x

using namespace std;
const int N = 10000 + 5;
const int B = pow(N, 2.0/3);
struct Q {
    int l, r, t, id;
    bool operator < (const Q &b) const {
        if(l/B==b.l/B) {
            if(r/B==b.r/B) return t<b.t;
            else return r/B<b.r/B;
        }else return l/B<b.l/B;
    }
}q[N];
struct M {
    int p, x, y;
} md[N];//保存修改序列
int a[N], n, m, k, tim, tans, ans[N], cnt[N], b[N];//a原序列,b辅助数组
int l=1, r=0, t=0;
void update(int x, int f) {
    cnt[x]+=f;
    if(cnt[x]==0&&f==-1) tans--;
    if(cnt[x]==1&&f==1) tans++;
}
void updateTime(int t, int f) {
    if(f==1) {
        if(l<=md[t].p&&md[t].p<=r) update(md[t].x, -1), update(md[t].y, 1);
        a[md[t].p]=md[t].y;
    }else {
        if(l<=md[t].p&&md[t].p<=r) update(md[t].y, -1), update(md[t].x, 1);
        a[md[t].p]=md[t].x;
    }
}
int main() {
    cin>>n>>m;
    for (int i=1; i<=n; i++) {
        cin>>a[i];
        b[i]=a[i];
    }
    for (int i=1; i<=m; i++) {
        int op, l, r;
        cin>>op>>l>>r;
        if (op==1) q[++k]={l, r, tim, k};
        else {
            md[++tim]={l, b[l], r};
            b[l]=r;
        }
    }
    sort(q+1, q+k+1);
    for (int i=1; i<=k; i++) {
        while(t<q[i].t) updateTime(++t, 1);
        while(t>q[i].t) updateTime(t--, -1);
        while(l<q[i].l) update(a[l++], -1);
        while(l>q[i].l) update(a[--l], 1);
        while(r<q[i].r) update(a[++r], 1);
        while(r>q[i].r) update(a[r--], -1);
        ans[q[i].id] = tans;
    }
    for (int i=1; i<=k; i++) printf("%d\n", ans[i]);
    return 0;
}

笛卡尔树

886
请添加图片描述


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值