牛客练习赛29E 位运算?位运算! splay

15 篇文章 0 订阅

Description


请实现一个数据结构支持以下操作:
区间循环左右移,区间与,区间或,区间求和。

所有操作形如 opt l r v。
opt=1 表示将区间[l,r]循环右移v位。
opt=2 表示将区间[l,r]循环左移v位。
opt=3 表示将区间[l,r]按位或上v。
opt=4 表示将区间[l,r]按位与上v。
opt=5 询问区间[l,r]的和。
保证opt=1或2时 1 ≤ v ≤ 20
注意:为了优化你的做题体验,操作5也会输入一个v,但是是没有意义的。
注意:循环左右移在20个二进制位的意义下进行

1 ≤ N,Q ≤ 2*105 0 ≤ ai < 220
一些说明:

  1. 对于00000000000000000101,右移一位后会变成10000000000000000010
  2. 不是区间位移,是区间中的每一个数的二进制位的位移

Solution


心态崩了,splay的做法常数升天,调不出来打出gg

考虑套路做法。我们把二进制的每一位单独处理,区间&和|就按位讨论,区间求和就是统计每一位上1的数量*这一位贡献的和,区间左右移就是依次交换这些区间
注意到1、2操作的特殊性,我们可以 (不可以) 用splay维护20个区间,每次交换就切出来贴在目标位置上。这样做理论上是nlog^2n的

然鹅,然鹅我的代码常数巨大,2e5的数据要跑15s。人生几何,不如写一些不那么恶心的题目。。

Code


#include <stdio.h>
#include <string.h>
#include <bitset>
#define rep(i,st,ed) for (register int i=st,_=ed;i<=_;++i)
#define drp(i,st,ed) for (register int i=st,_=ed;i>=_;--i)

typedef long long LL;
const int N=200005;

struct treeNode {
	int son[2],fa,size,tag,sum,val;
} t[N*22];

int a[N],root[21],last[21];
int w[N],wjp;

LL bin[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void push_up(int x) {
	if (!x) return ;
	t[x].size=t[t[x].son[0]].size+t[t[x].son[1]].size+1;
	t[x].sum=t[x].val+t[t[x].son[0]].sum+t[t[x].son[1]].sum;
}

void push_down(int x) {
	if (t[x].tag==-1) return ;
	int w=t[x].tag; t[x].tag=-1;
	if (t[x].son[0]) {
		t[t[x].son[0]].tag=t[t[x].son[0]].val=w;
		t[t[x].son[0]].sum=t[t[x].son[0]].size*w;
	}
	if (t[x].son[1]) {
		t[t[x].son[1]].tag=t[t[x].son[1]].val=w;
		t[t[x].son[1]].sum=t[t[x].son[1]].size*w;
	}
}

void rotate(int x) {
	int y=t[x].fa; int z=t[y].fa;
	int k=t[y].son[1]==x;
	t[z].son[t[z].son[1]==y]=x; t[x].fa=z;
	t[y].son[k]=t[x].son[!k]; t[t[x].son[!k]].fa=y;
	t[x].son[!k]=y; t[y].fa=x;
	push_up(y); push_up(x);
}

void splay(int &root,int x,int goal=0) {
	for (;t[x].fa!=goal;) {
		int y=t[x].fa; int z=t[y].fa;
		if (z!=goal) {
			if ((t[y].son[1]==x)^(t[z].son[1]==y)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
	if (!goal) root=x;
}

int kth(int root,int k) {
	int x=root;
	for (;233;) {
		push_down(x);
		if (t[t[x].son[0]].size+1==k) return x;
		if (t[t[x].son[0]].size>=k) x=t[x].son[0];
		else {
			k-=t[t[x].son[0]].size+1;
			x=t[x].son[1];
		}
	}
}

int cut(int &root,int l,int r) {
	int L=kth(root,l-1);
	int R=kth(root,r+1);
	splay(root,L); splay(root,R,root);
	int ret=t[R].son[0];
	t[ret].fa=t[R].son[0]=0;
	push_up(R); push_up(L);
	return ret;
}

int build(int l,int r) {
	int mid=(l+r)>>1;
	int pos=mid+wjp;
	t[pos].tag=-1;
	t[pos].size=1;
	t[pos].val=w[mid-1];
	if (l<mid) {
		t[pos].son[0]=build(l,mid-1);
		t[t[pos].son[0]].fa=pos;
	}
	if (mid<r) {
		t[pos].son[1]=build(mid+1,r);
		t[t[pos].son[1]].fa=pos;
	}
	push_up(pos);
	return pos;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int n=read(),q=read();
	rep(i,1,n) a[i]=read();
	bin[0]=1; rep(i,1,19) bin[i]=bin[i-1]*2;
	rep(j,0,19) {
		rep(i,1,n) w[i]=(a[i]&bin[j])!=0;
		root[j]=build(1,n+2);
		wjp+=n+2;
	}
	for (;q--;) {
		int opt=read(),l=read(),r=read(),v=read();
		if (opt==2) {
			rep(i,0,v-1) last[i]=cut(root[i],l+1,r+1);
			rep(i,v,19) {
				int rt=root[i];
				int now=cut(rt,l+1,r+1);
				t[t[rt].son[1]].son[0]=last[i-v];
				t[last[i-v]].fa=t[rt].son[1];
				push_up(t[rt].son[1]); push_up(rt);
				last[i]=now; root[i]=rt;
			}
			rep(i,0,v-1) {
				int rt=root[i];
				t[t[rt].son[1]].son[0]=last[i+20-v];
				t[last[i+20-v]].fa=t[rt].son[1];
				push_up(t[rt].son[1]); push_up(rt);
				root[i]=rt;
			}
		} else if (opt==1) {
			drp(i,19,20-v) last[i]=cut(root[i],l+1,r+1);
			drp(i,19-v,0) {
				int rt=root[i];
				int now=cut(rt,l+1,r+1);
				int rs=t[rt].son[1];
				t[t[rt].son[1]].son[0]=last[i+v];
				t[last[i+v]].fa=t[rt].son[1];
				push_up(t[rt].son[1]); push_up(rt);
				last[i]=now; root[i]=rt;
			}
			drp(i,19,20-v) {
				int rt=root[i]; int rs=t[rt].son[1];
				t[rs].son[0]=last[i-20+v];
				t[last[i-20+v]].fa=rs;
				push_up(rs); push_up(rt);
				root[i]=rt;
			}
		} else if (opt==3) {
			rep(i,0,19) if (v&bin[i]) {
				int rt=root[i];
				splay(rt,kth(rt,l));
				splay(rt,kth(rt,r+2),rt);
				int tmp=t[t[rt].son[1]].son[0];
				t[tmp].tag=t[tmp].val=1;
				t[tmp].sum=t[tmp].size;
				push_up(t[rt].son[1]); push_up(rt);
				root[i]=rt;
			}
		} else if (opt==4) {
			rep(i,0,19) if (!(v&bin[i])) {
				int rt=root[i];
				splay(rt,kth(rt,l));
				splay(rt,kth(rt,r+2),rt);
				int rs=t[rt].son[1];
				int tmp=t[rs].son[0];
				t[tmp].tag=t[tmp].val=t[tmp].sum=0;
				push_up(rs); push_up(rt);
				root[i]=rt;
			}
		} else {
			LL ans=0;
			rep(i,0,19) {
				int rt=root[i];
				splay(rt,kth(rt,l));
				splay(rt,kth(rt,r+2),rt);
				ans+=bin[i]*t[t[t[rt].son[1]].son[0]].sum;
				root[i]=rt;
			}
			printf("%lld\n", ans);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值