二进制与、平方和(河南省2020CCPC J)

二进制与、平方和

时间限制: 3 S e c 3 Sec 3Sec 内存限制: 512 M B 512 MB 512MB

题目描述

请你维护一个长度为 n 的非负整数序列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,支持以下两种操作:

  1. 第一种操作会将序列 a l , a l + 1 , … , a r a_l,a_{l+1},…,a_r al,al+1,,ar 中的每个元素,修改为各自和 x x x 的"二进制与"(Bitwise binary AND)的值,其中 l , r , x l,r,x l,r,x 在每次操作时会给定;
  2. 第二种操作会询问序列 a l , a l + 1 , … , a r a_l,a_{l+1},…,a_r al,al+1,,ar 中所有元素的平方和模 998244353 998244353 998244353的值,即 ∑ i = l r a i 2 \sum_{i=l}^ra_i^2 i=lrai2 998244353 998244353 998244353,其中 l , r l,r l,r在每次操作时会给定。

总共有 q q q 次操作,请你在维护序列的过程中,输出第二种操作所询问的答案。注意需要取模。

输入

第一行,一个整数 n ( 1 ≤ n ≤ 3 × 1 0 5 ) n (1≤n≤3×10^5) n(1n3×105),表示序列的长度。
第二行,共 n n n 个整数 a i ( 0 ≤ a i < 2 24 ) a_i (0≤a_i<2^{24}) ai(0ai<224),表示序列。
第三行,一个整数 q ( 1 ≤ q ≤ 3 × 1 0 5 ) q (1≤q≤3×10^5) q(1q3×105),表示询问的数量。
接下来 q q q 行,每行表示一个操作,输入有两种格式:

  1. 第一种操作的格式为 “1 l r x”,表示将序列中编号在区间 [ l , r ] [l,r] [l,r] 的所有元素,修改为和 x x x 二进制与操作后的值,其中 1 ≤ l ≤ r ≤ n 1≤l≤r≤n 1lrn, 0 ≤ x < 2 24 0≤x<2^{24} 0x<224
  2. 第二种操作的格式为 “2 l r”,表示询问序列中编号在区间 [ l , r ] [l,r] [l,r] 的所有元素的平方和,模 998244353 998244353 998244353 的值,其中 1 ≤ l ≤ r ≤ n 1≤l≤r≤n 1lrn

数据保证至少有一个第二种操作,即保证至少询问一次答案。

输出

每当遇到第二种操作时,输出询问的答案。注意需要取模。

样例输入

3
13 31 28
4
2 1 3
1 3 3 25
1 1 2 18
2 2 3

样例输出

1914
900

样例输入二

5
9 11 12 5 1
7
2 1 3
1 3 3 0
1 1 3 9
1 4 5 13
2 1 3
1 4 5 14
2 1 5

样例输出二

346
162
178

样例输入三

4
16777215 16777215 16777215 16777214
4
2 2 2
1 1 4 16777214
2 1 4
2 3 4

样例输出三

981185168
795789897
897017125

“二进制与”(Bitwise binary AND)结果的第$ i$ 个二进制位为 1,当且仅当两个操作数的第$ i$ 位都为 1。

2020年河南省CCPC大学生程序设计竞赛

思路:

线段树维护区间内的数每个位上的0和1的总个数和区间平方和。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 3e5+5, mod = 998244353; 
ll q[maxn];
int n,m;
struct Node {
	int l,r;
	ll sum; 		//区间平方和 % mod
	int x[25];		//维护当前区间各个位上0和1的个数 
}tr[maxn*4];


void pushup(int u)
{
	tr[u].sum = (tr[u<<1].sum + tr[u<<1|1].sum) % mod;
	for(int i=0;i<25;++i)
	{
		tr[u].x[i] = tr[u<<1].x[i] + tr[u<<1|1].x[i];
	}
}


void build(int u,int l,int r)
{
	if(l == r) {
		tr[u] = {l, r,q[r]*q[r]%mod};
		for(int i=0;i<25;++i)
		{
			tr[u].x[i] = q[r] >> i & 1;
		}
		return;
	}
	tr[u] = {l, r};
	int mid = l + r >> 1;
	build(u<<1,  l,  mid);
	build(u<<1|1,mid+1,r);
	pushup(u);
}

void modify(int u,int l,int r,ll x)
{
	//修改根节点 
	if(tr[u].l == tr[u].r) 
	{
		ll sum = 0;
		for(int i=0;i<25;++i)
		{
			if(tr[u].x[i] && (x>>i&1)) //都为1 
				sum |= 1 << i; //加上这一位
				//sum += 1 << i; //加上这一位
			else tr[u].x[i] = 0; 
		}
		tr[u].sum = sum * sum % mod;
	}
	if(l <= tr[u].l && tr[u].r <= r)
	{
		//每一位有四种情况 :
		// 本来是1 ,x为0  ,需要修改  √ 
		// 本来是1 ,x为1  ,不需要修改 
		// 本来是0 ,x为1  ,不需要修改 
		// 本来是0, x为0  ,不需要修改 
		bool f = true; //是否不需要修改 	
		for(int i=0;i<25;++i)
			//如果当前位置存在1 ,并且x的这一位是0
			if(tr[u].x[i] && (x>>i&1)==0)
			{
				f = false; //需要修改子树 
				break;
			}
		if(f) return; //如果不需要修改 
	}
	int mid = tr[u].l + tr[u].r >> 1;
	if(l <= mid) modify(u<<1,  l,r,x);
	if(r >  mid) modify(u<<1|1,l,r,x);
	pushup(u); 
}

ll query(int u,int l,int r)
{
	if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
	int mid = tr[u].l + tr[u].r >> 1;
	int res = 0;
	if(l <= mid) res =        query(u<<1,  l,r)  % mod;
	if(r >  mid) res = (res + query(u<<1|1,l,r)) % mod;
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%lld",&q[i]);
	build(1,1,n); 
	scanf("%d",&m);
	int op,l,r,x;
	while(m--)
	{
		scanf("%d%d%d",&op,&l,&r);
		if(op == 1)
		{
			scanf("%d",&x);
			modify(1,l,r,x);
		}
		else
		{
			printf("%lld\n",query(1,l,r)); 
		}
	}
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值