【COGS-2638】数列操作ψ 线段树

题目链接:

  http://cogs.pro/cogs/problem/problem.php?pid=2638

Solution

  用jry推荐的写法即可做到单次$O(logN/log^{2}N)$。

  具体的就是维护一个区间的$same$,其二进制下01的意义表示区间中所有数的二进制位第$k$位是否相同。

  处理标记,当整个区间的 需要修改 的二进制位相同时即可直接打上标记。

  然后就是正常搞了啊..其实当满足上述的情况后修改就可以变成区间加减标记来处理了,只需要一个标记即可,常数会更优一点。

Code:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
	while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
	return x*f;
}

#define MAXN 100010

int N,M,a[MAXN];

#define AllSame ((1<<30)-1)

struct SgtNode{
	int l,r,otag,atag,same,maxx;
}tree[MAXN<<2];

inline void Update(const int &now)
{
	tree[now].maxx=max(tree[now<<1].maxx,tree[now<<1|1].maxx);
	tree[now].same=( tree[now<<1].same & tree[now<<1|1].same ) & ( tree[now<<1].maxx ^ (~tree[now<<1|1].maxx) );
}

inline void Build(const int &now,const int &l,const int &r)
{
	tree[now].l=l; tree[now].r=r;
	tree[now].otag=0; tree[now].atag=AllSame;
	if (l==r) {
		tree[now].maxx=a[l]; tree[now].same=AllSame;
		return;
	}
	int mid=(l+r)>>1;
	Build(now<<1,l,mid); Build(now<<1|1,mid+1,r);
	Update(now);
}

inline void And(const int &now,const int &val) {tree[now].maxx&=val; tree[now].otag&=val; tree[now].atag&=val;}

inline void Or(const int &now,const int &val) {tree[now].maxx|=val; tree[now].otag|=val; tree[now].atag|=val;}

inline void Pushdown(const int &now)
{
	if (tree[now].l==tree[now].r || (!tree[now].otag && tree[now].atag==AllSame)) return;
	int ot=tree[now].otag,at=tree[now].atag;
	tree[now].otag=0; tree[now].atag=AllSame;
	if (ot) Or(now<<1,ot),Or(now<<1|1,ot);
	if (at!=AllSame) And(now<<1,at),And(now<<1|1,at);
}

inline bool CheckOr(const int &same,const int &val) {return (same&val)==val;}

inline void ModifyOr(const int &now,const int &L,const int &R,const int &val)
{
	int l=tree[now].l,r=tree[now].r;
	if (L>r || R<l) return;
	if (L<=l && R>=r && CheckOr(tree[now].same,val)) {
		Or(now,val);
		return;
	}
	Pushdown(now);
	ModifyOr(now<<1,L,R,val);
	ModifyOr(now<<1|1,L,R,val);
	Update(now);
}

inline bool CheckAnd(const int &same,const int &val) {return (~same&AllSame|val)==val;}

inline void ModifyAnd(const int &now,const int &L,const int &R,const int &val)
{
	int l=tree[now].l,r=tree[now].r;
	if (L>r || R<l) return;
	if (L<=l && R>=r && CheckAnd(tree[now].same,val)) {
		And(now,val);
		return;
	}
	Pushdown(now);
	ModifyAnd(now<<1,L,R,val);
	ModifyAnd(now<<1|1,L,R,val);
	Update(now);
}

inline int Query(const int &now,const int &L,const int &R)
{
	int l=tree[now].l,r=tree[now].r;
	if (L<=l && R>=r) {
		return tree[now].maxx;
	}
	Pushdown(now);
	int mid=(l+r)>>1,re=0;
	if (L<=mid) re=Query(now<<1,L,R);
	if (R>mid) re=max(re,Query(now<<1|1,L,R));
	return re;
}

int main()
{
	freopen("series_wei.in","r",stdin);
	freopen("series_wei.out","w",stdout);

	N=read(),M=read();
	for (int i=1; i<=N; i++) a[i]=read();
	Build(1,1,N);
	while (M--) {
		int opt=read(),l=read(),r=read(),val;
		switch (opt) {
			case 1: val=read(); ModifyAnd(1,l,r,val); break;
			case 2: val=read(); ModifyOr(1,l,r,val); break;
			case 3: printf("%d\n",Query(1,l,r)); break;
		}
	}
	return 0;
}
/*
8 6
4 0 5 7 2 9 12 8
2 2 5 15
1 3 5 2
3 5 7
1 5 7 12
2 1 6 4
3 2 6
*/

  

时间: 2024-10-21 22:22:42

【COGS-2638】数列操作ψ 线段树的相关文章

COGS 2638. 数列操作ψ 线段树

传送门 : COGS 2638. 数列操作ψ 线段树 这道题让我们维护区间最大值,以及维护区间and,or一个数 我们考虑用线段树进行维护,这时候我们就要用到吉司机线段树啦 QAQ 由于发现若干次and,or之后,如果数据分布均匀,那么几乎所有的数在若干次操作后都会变成同一个数 因为我们的and操作中的0位,以及or操作当中的1位,都是可以把整个区间的那一二进制位重置为相同的 我们考虑利用这一个性质 如果我们直接维护一个区间内的值是否是相同的,那么效果会差很多. 我们发现我们在进行and操作的时

bzoj1789 AHOI 维护数列(线段树)

首先想到线段树,然后刚开始写忽然想到树状数组求和岂不是更快,而且编程复杂度又小,于是把之前写的删掉,写树状数组,写完模版之后忽然发现这题竟然是区间修改! 于是又删掉重写,忽然发现不会处理又加又乘的,果断看题解-- 经过几乎两个小时的调试,终于1A. 需要注意的是,一定要让线段树的每一个区间保存的值时刻为正确的,这样才能在调用时直接返回.比如说这道题的change和query操作的最后一句话: sum:=f(g[k<<1]+g[k<<1+1]) 而不是 sum:=f(t[k<&

BZOJ_4636_蒟蒻的数列_线段树+动态开点

Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一行一个整数N,然后有N行,每行三个正整数a.b.k. N<=40000 , a.b.k<=10^9 Output 一个数,数列中所有元素的和 Sample Input 4 2 5 1 9 10 4 6

[bzoj4636]蒟蒻的数列_线段树

蒟蒻的数列 bzoj-4636 题目大意:给定一个序列,初始均为0.n次操作:每次讲一段区间中小于k的数都变成k.操作的最后询问全局和. 注释:$1\le n\le 4\cdot 10^4$. 想法:那个操作就是一个不好好说话的操作,说白了就是对区间的每一个数取max 然后我们对于那个序列建立分治线段树.每个操作我都把它挂在对应的log的点上. n个操作都执行完了之后我们从1号节点深搜,更新答案即可. 最后,附上丑陋的代码... ... #include <iostream> #include

无聊的数列【线段树】

题目背景 无聊的YYB总喜欢搞出一些正常人无法搞出的东西.有一天,无聊的YYB想出了一道无聊的题:无聊的数列...(K峰:这题不是傻X题吗) 题目描述 维护一个数列{a[i]},支持两种操作: 1.1 L R K D:给出一个长度等于R-L+1的等差数列,首项为K,公差为D,并将它对应加到a[L]~a[R]的每一个数上.即:令a[L]=a[L]+K,a[L+1]=a[L+1]+K+D, a[L+2]=a[L+2]+K+2D--a[R]=a[R]+K+(R-L)D. 2.2 P:询问序列的第P个数

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个

bzoj 1858 序列操作(线段树)

题外话 本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= = 然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ Description: 给定一个0/1序列,有如下5个操作: 0:区间赋值为0 1:区间赋值为1 2:区间取反 3:询问区间内1的个数 4:询问区间内最大连续1的个数 Solution 没有操作4这显然就是个SB题,有了操作4我们需要打几个标记 我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数 区间内0/1个数,区间内最大连续

BZOJ 1858 SCOI2010 序列操作 线段树

题目大意:给定一个01序列,提供三种操作: 0:把一段区间的全部元素都变成0 1:把一段区间的全部元素都变成1 2:把一段区间内的全部元素全都取反 3:查询一段区间内1的个数 4:查询一段区间内最长的一段连续的1 首先假设没有操作4这就是bitset的水题... 多了这个,我们考虑线段树 线段树的每个节点存改动标记和翻转标记,以及该区间的信息 尽管查询的信息都是1 可是我们要连0一起保存 由于翻转后0就变成了1 1就变成了0 区间信息包含: 左端点的元素 右端点的元素 左端点開始的最长的连续元素

bzoj 2333 棘手的操作(线段树)

题外话 昨天粗去浪了一天,打麻将输了一下午真是拙计啊 这个题号2333真是2333,有爱无比 晚上回家把这题写了,1A,还算不错 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y:加一条边,连接第x个节点和第y个节点 A1 x v:将第x个节点的权值增加v A2 x v:将第x个节点所在的连通块的所有节点的权值都增加v A3 v:将所有节点的权值都增加v F1 x:输出第x个节点当前的权值 F2 x: