[FOJ 2105][Vjudge 35179] Digits Count [线段树]

给一列数,可以让第l到r个数同时与、或、异或上某个数,或者求第l到r个数的和

数据范围:总共10^6个数,10^5次操作,每个数的大小在[0,16)内

把每个数都按照二进制分成4位,建立4棵线段树,线段树上有置零(与操作),置一(或操作),取反(异或操作)三种操作,有求和查询

#include <cstring>
#include <cstdio>

struct Node {
	int l,r,sum,lazy;
	Node *ls,*rs;
	Node () {}
	Node (int ll,int rr) {
		l=ll;r=rr;
		sum=0;lazy=0;
		ls=rs=NULL;
	}
	void *operator new (size_t,void *p) {
		return p;
	}
	void getLazy(int x) {
		if (x==0) return;
		else if (lazy==0) lazy=x;
		else if (x==1) lazy=1;
		else if (x==2) lazy=2;
		else if (lazy==1) lazy=2;
		else if (lazy==2) lazy=1;
		else lazy=0;
	}
	void down() {
		if (ls!=NULL) ls->getLazy(lazy);
		if (rs!=NULL) rs->getLazy(lazy);
		if (lazy==1) sum=r-l+1;
		else if (lazy==2) sum=0;
		else if (lazy==3) sum=r-l+1-sum;
		lazy=0;
	}
};
struct SegTree {
	Node a[2100000];
	Node *root,*las,*tmp;
	void clear(int x[],int n) {
		las=a;
		for (int i=0;i<n;i++) {
			tmp=new(las++)Node(i,i);
			tmp->sum=x[i];
		}
		root=makeTree(0,n-1);
	}
	Node *makeTree(int l,int r) {
		if (l==r) return a+l;
		Node *ans=new(las++)Node(l,r);
		int t=(l+r)/2;
		ans->ls=makeTree(l,t);
		ans->rs=makeTree(t+1,r);
		ans->sum=ans->ls->sum+ans->rs->sum;
		return ans;
	}
	void set(Node *from,int l,int r,int opt) {
		if (from->l==l&&from->r==r) {
			from->getLazy(opt);
			return;
		}
		from->down();
		int t=(from->l+from->r)/2;
		if (t>=r) set(from->ls,l,r,opt);
		else if (t<l) set(from->rs,l,r,opt);
		else {
			set(from->ls,l,t,opt);
			set(from->rs,t+1,r,opt);
		}
		from->ls->down();
		from->rs->down();
		from->sum=from->ls->sum+from->rs->sum;
	}
	int get(Node *from,int l,int r) {
		from->down();
		if (from->l==l&&from->r==r) return from->sum;
		int t=(from->l+from->r)/2;
		if (t>=r) return get(from->ls,l,r);
		else if (t<l) return get(from->rs,l,r);
		else return get(from->ls,l,t)+get(from->rs,t+1,r);
	}
};

int n,m;
SegTree a[4];
int b[1000000];
int c[1000000];

int main() {
	int t,i,j,opn,l,r,ans;
	char s[10];
	scanf("%d",&t);
	while (t--) {
		scanf("%d%d",&n,&m);
		for (i=0;i<n;i++) scanf("%d",&b[i]);
		for (i=0;i<4;i++) {
			for (j=0;j<n;j++) c[j]=((b[j]&(1<<i))!=0);
			a[i].clear(c,n);
		}
		for (i=0;i<m;i++) {
			scanf("%s",s);
			if (s[0]=='A') {
				scanf("%d%d%d",&opn,&l,&r);
				for (j=0;j<4;j++) {
					if ((opn&(1<<j))==0) a[j].set(a[j].root,l,r,2);
				}
			} else if (s[0]=='O') {
				scanf("%d%d%d",&opn,&l,&r);
				for (j=0;j<4;j++) {
					if ((opn&(1<<j))!=0) a[j].set(a[j].root,l,r,1);
				}
			} else if (s[0]=='X') {
				scanf("%d%d%d",&opn,&l,&r);
				for (j=0;j<4;j++) {
					if ((opn&(1<<j))!=0) a[j].set(a[j].root,l,r,3);
				}
			} else {
				scanf("%d%d",&l,&r);
				ans=0;
				for (j=0;j<4;j++) {
					//printf("%d ",a[j].get(a[j].root,l,r));
					ans+=a[j].get(a[j].root,l,r)*(1<<j);
				}
				//printf("\n");
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值