luogu p7492 序列

原题链接:
序列 - 洛谷icon-default.png?t=LA92https://www.luogu.com.cn/problem/P7492

题目背景

disangan233 正在数数,他希望你帮他记录数数的序列,并完成一些操作。

题目描述

你有一个长为 n 的序列 a,现在要对其进行 m 次操作。操作分为两种:

  1. 给定两个整数 l,r,表示询问 l 到 r 的最大连续子段和。
  2. 给定三个整数 l,r,k,表示将 l 到 r 的 ai​ 都按位或上一个 k。

对于所有数据,n,m≤10^5,−2^30≤ai​,k<2^30,1≤l≤r≤n。

注意:负数按照 32 位补码取按位或。

输入格式

输入共 m+2 行。

第 1 行输入 2 个正整数 n,m。

第 2 行输入 n 个整数 a1​…an​。

接下来 m 行,每行先输入 1 个正整数 op,op=1 输入 2 个整数 l,r 表示一次询问,否则输入 3 个整数 l,r,k 表示一次修改。

输出格式

输出共若干行,每行共 1 个整数,表示询问的答案。

输入输出样例

输入 #1复制

15 15
512 -65 33554432 32 8194 13 16 2 67108872 131072 -8192 8194 16 2048 4096 
1 3 5
1 10 10
2 1 7 671367424
1 8 14
1 5 11
2 13 13 335579137
2 2 13 5376
1 2 5
2 5 6 8392768
1 1 2
2 2 14 201335872
2 1 14 0
1 11 12
1 8 12
1 4 9

输出 #1复制

33562658
131072
67242012
2081350441
2047680290
671367936
201340226
805489228
3373416393

思路:

        如果你还不知道线段树是啥可以先做:【模板】线段树 1 - 洛谷icon-default.png?t=LA92https://www.luogu.com.cn/problem/P3372
【模板】线段树 2 - 洛谷https://www.luogu.com.cn/problem/P3373        如果你还不会线段树求最大子段和,可以先做:

        但注意:这题需要注册spoj账号并绑定才可以交,,spoj账号需要代理才能注册GSS1 - Can you answer these queries I - 洛谷https://www.luogu.com.cn/problem/SP1043        

      本题在修改过程中如果使用暴力单点或操作肯定是会超时,那么该怎么样操作才可以使时间复杂度降下来呢

        因为按位或的性质,已经变成1的位置无论怎么或都不可能再改变为0了。那么已经被或为全1的数,无论再怎么或也是无意义的或了.但是除了这样还是不够的。

        那么还有什么情况是属于无意义的或操作呢,将一个数拆分为二进制数

        如   十进制数 114514

        转换为二进制之后为

                                                          11011111101010010

        我们发现,如果一个数能将其0位变成1位则才是有意义的或操作

        我们可以使用一个标记数opt为该数取反之后的数,则若将要被或上的数&opt为0,说明这个将要被或上的数没有办法填补该数的任何一个“坑位”,那自然就是无意义的或了

        opt的pushup:

        在下放将要被或上的数x时,只要左孩子或右孩子有坑位,就自然是可以或上的,那么当前结点的坑位自然就是左孩子的opt | 右孩子的opt

         我们以左右孩子为叶子结点举例假设左孩子的数为114514,右孩子的数为110550,那么左孩子的二进制数为11011111101010010,右孩子的二进制数为11010111111010110

 则:

                                        左孩子的opt:00100000010101101

                                        右孩子的opt:00101000000101001

     父结点的opt(左孩子opt|右孩子opt):00101000010101101

  可以明显看出,父结点完美继承了左右孩子的"坑位"

  而剩余的就是求线段树最大子段和了

  ACcode:

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
inline ll read()
{
    ll f = 1, x = 0; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar();}
    return f * x;
}
struct node{
	ll ans,lans,rans,sum,opt;
	node(){
		ans = lans = rans = sum = opt = 0;
	}
}t[400005];
int a[100005];
int n,m,l,r,x;
void pushup(int now)
{
	t[now].sum = t[now<<1].sum+t[now<<1|1].sum;
	t[now].lans = max(t[now<<1].lans,t[now<<1].sum+t[now<<1|1].lans);
	t[now].rans = max(t[now<<1|1].rans,t[now<<1|1].sum+t[now<<1].rans);
	t[now].ans = max(max(t[now<<1].ans,t[now<<1|1].ans),t[now<<1].rans+t[now<<1|1].lans);
	t[now].opt = t[now<<1].opt|t[now<<1|1].opt;//继承左右孩子的坑位 
}

void build(int start,int end,int now)
{
	if(start==end)
	{
		t[now].ans = t[now].lans = t[now].rans = max(0ll,a[start]);
		t[now].sum = a[start];
		t[now].opt = ~a[start];
		return;
	}
	int mid = (start+end)>>1;
	build(start,mid,now<<1);
	build(mid+1,end,now<<1|1);
	pushup(now);
}

void update(int start,int end,int now)
{
	if(l<=start&&r>=end)
	{
		if(!(t[now].opt&x)) return;//在本结点已经没有x需要或的坑位了 
		if(start==end)//已经到叶子结点了 
		{
			a[start]|=x;
			t[now].ans = t[now].lans = t[now].rans = max(0ll,a[start]);
			t[now].sum = a[start];
			t[now].opt = ~a[start];
			return;
		}
		int mid = (start+end)>>1;
		if(t[now<<1].opt&x) update(start,mid,now<<1);//如果左孩子有x的坑位就下放 
		if(t[now<<1|1].opt&x) update(mid+1,end,now<<1|1);//如果右孩子有x的坑位就下放 
		pushup(now);
		return;
	}
	int mid = (start+end)>>1;
	if(l<=mid&&t[now<<1].opt&x)	update(start,mid,now<<1);
	if(r>mid&&t[now<<1|1].opt&x)	update(mid+1,end,now<<1|1);
	pushup(now);
}
node query(int start,int end,int now)
{
	if(l<=start&&r>=end)
		return t[now];
	int mid = (start+end)>>1;
	if(l>mid)	return query(mid+1,end,now<<1|1);
	else if(r<=mid)	return query(start,mid,now<<1);
	else{
		node aa = query(start,mid,now<<1),bb = query(mid+1,end,now<<1|1);
		node aans;
		aans.sum = aa.sum+bb.sum;
		aans.lans = max(aa.lans,aa.sum+bb.lans);
		aans.rans = max(aa.rans+bb.sum,bb.rans);
		aans.ans = max(max(aa.ans,bb.ans),aa.rans+bb.lans);
		return aans;
	}
}

signed main()
{
	n = read(),m=read();
	for(int i = 1;i<=n;++i)
		a[i] = read();
	build(1,n,1);
	while(m--)
	{
		int o = read();
		if(o==1)
		{
			l = read();
			r = read();
			printf("%lld\n",query(1,n,1).ans);
		}
		else
		{
			l = read();
			r = read();
			x = read();
			update(1,n,1);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值