【JZOJ A组】树

Description

在这里插入图片描述

Input

第一行一个整数 n 表示序列长度, 接下来一行 n 个整数描述这个序列.
第三行一个整数 q 表示操作次数, 接下来 q 行每行一次操作, 格式同题目描述.

Output

   输出等同于操作 2, 3 次数之和的行数, 每行一个非负整数表示对应询问的答案. 注意操作 2 的答案不需要进行取模.

Sample Input

Sample Input1
5
8 4 3 5 6
5
2 3 5
3 1 2
1 2 4 3
2 3 5
3 1 2

样例 2
见下发文件中的 ex_seg2.in/out.

Sample Output

Sample Output1
14
608
10
384

样例 1 解释
第三次操作后, 序列变为 [8, 0, 3, 1, 6].

Data Constraint

对于前 30% 的数据, n, q ≤ 100;
对于另 20% 的数据, 没有操作 1;
对于另 20% 的数据, 没有操作 3;
对于 100% 的数据, n, q ≤ 10^5, ai ≤ 10^9, k ≤ 2^30, 1 ≤ l ≤ r ≤ n.

思路

修改只会让数变小, 每个数只会变小 log 次, 所以我们线段树维护区间或起来的值判断是否需要修改, 如果需要就暴力下去修改. 复杂度 O(nlog2n)
对于操作 3 直接把式子展开, 再维护一个区间平方和即可.

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

#define N 100010
#define ll long long
#define Ull unsigned long long

const ll P=998244353;

struct T
{
	ll sqr,sum,orr;
};

T tree[N*4];
ll a[N];
int n,q;

T up(T x,T y)
{
	T z;
	z.orr=x.orr|y.orr;
	z.sum=x.sum+y.sum;
	z.sqr=(x.sqr+y.sqr)%P;
	return z;
}

void build(int x,int l,int r)
{
	if (l==r)
	{
		tree[x].sum=a[l];
		tree[x].sqr=(a[l]%P*a[l]%P)%P;
		tree[x].orr=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(x+x,l,mid);
	build(x+x+1,mid+1,r);
	tree[x]=up(tree[x+x],tree[x+x+1]);
}

void change(int x,int l,int r,int s,int e,ll k)
{
	int mid=(l+r)/2;
	if (l==s && r==e)
	{
		if (l==r) 
		{
			a[l]&=k;		
			tree[x].sum=a[l];
			tree[x].sqr=(a[l]%P*a[l]%P)%P;
			tree[x].orr=a[l];
			return;
		}
		if ((tree[x+x].orr&k)!=tree[x+x].orr) change(x+x,l,mid,l,mid,k);
		if ((tree[x+x+1].orr&k)!=tree[x+x+1].orr) change(x+x+1,mid+1,r,mid+1,r,k);	
		tree[x]=up(tree[x+x],tree[x+x+1]);
		return;
	}
	if (e<=mid) {if ((tree[x+x].orr&k)!=tree[x+x].orr) change(x+x,l,mid,s,e,k);}
	  else 
			if (s>mid) {if ((tree[x+x+1].orr&k)!=tree[x+x+1].orr) change(x+x+1,mid+1,r,s,e,k);}
	    	else
	    	{
	    		if ((tree[x+x].orr&k)!=tree[x+x].orr) change(x+x,l,mid,s,mid,k);
	    		if ((tree[x+x+1].orr&k)!=tree[x+x+1].orr) change(x+x+1,mid+1,r,mid+1,e,k);
	   	  }
	tree[x]=up(tree[x+x],tree[x+x+1]);
}

T find(int x,int l,int r,int s,int e)
{
	if (l==s && r==e) return tree[x];
	int mid=(l+r)/2;
	if (e<=mid) return find(x+x,l,mid,s,e);
	  else if (s>mid) return find(x+x+1,mid+1,r,s,e);
		  else
			{
				T y=find(x+x,l,mid,s,mid);
				T z=find(x+x+1,mid+1,r,mid+1,e);
				return up(y,z);
			}
}

int main()
{
	freopen("seg.in","r",stdin);
	freopen("seg.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	  scanf("%lld",&a[i]);
	build(1,1,n);
	scanf("%d",&q);
	ll tt,l,r;
	ll k;
	for (int i=1;i<=q;i++)
	{
		scanf("%lld%lld%lld",&tt,&l,&r);
		if (tt==1) 
		{
			scanf("%lld",&k);
			change(1,1,n,l,r,k);
		}
		if (tt==2)
		{
			T s=find(1,1,n,l,r);
			printf("%lld\n",s.sum);
		}
		if (tt==3)
		{
			T s=find(1,1,n,l,r);
			printf("%lld\n",(2*(r-l+1)%P*s.sqr%P+2*(s.sum%P)%P*(s.sum%P)%P)%P);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值