题面

🔗 (提交:🔗,洛谷:🔗

有一个序列 { a n } \{a_n\} {an},初始时全为 0 0 0,有 q q q 次操作:

  • 1 l r x :对所有 l ≤ i ≤ r l\leq i\leq r lir ,若 a i = x a_i=x ai=x ,则将 a i a_i ai 增加 1 1 1
  • 2 l r :查询区间 [ l , r ] [l,r] [l,r] 最大值。

n , q , x ≤ 5 × 1 0 5 n,q,x\leq 5\times10^5 n,q,x5×105

6 s , 1024 m b 6s,1024mb 6s,1024mb

题解

大家好啊,我是退役后正在复健的大学选手,今天来点大家想看的东西啊

看到这道题,让人不禁往值域方向思考,诶?好像可以平衡树直接模拟?……要爆?启发式合并!冲刺!冲刺!

完了,然后呢?怎么查询?

要我枚举答案吗?好像只能这样了,那肯定爆啊?怎么办,放缩一下枚举范围吗,好像不太行得通欸。

分块?想得美。分治?拿来放缩都嫌少。

看起来只能把枚举给优化一下,怎么办,分块?二分?

你平衡树怎么把值域前后连接起来?你告诉我?

好,可以想想线段树合并,那也还是爆啊。

……

于是我做不出来咯,看了眼题解。

🤣👇🤣
👉🤡👈
🤣👆🤣

题解很简单,只需要每个值动态开点一个线段树,树上每个位置 1/0 表示 [是/否] 大于等于这个值,也就是把之前想的线段树做了一个后缀或。

这个东西,是可以直接维护的!

每次修改只需要将 x x x [ l , r ] [l,r] [l,r] 这段区间粘贴到 x + 1 x+1 x+1 上就可以了,最多新建 log ⁡ n \log n logn 个点,单次复杂度等同于区间查询。

二分答案的时候也是区间查询。

于是复杂度 log ⁡ 2 n \log^2n log2n

我不会说想不到这个做法,

但我会惊讶他们怎么想到的……

CODE

对了,线段树节点建议开到64倍

#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<random>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 500005
#define LL long long
#define LLL __int128
#define DB double
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
inline int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
inline LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s<'0'||s>'9') {if(s<0)return -1;if(s=='-')f=-f;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return;}
	if(x<0) putchar('-'),x=-x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
struct it{
	int ls,rs;
	int ct;
	it(){ls=rs=ct=0;}
}tre[MAXN*64];
int cnt;
int build(int l,int r) {
	int x = ++ cnt,md = (l+r)>>1;
	tre[x].ct = r-l+1;
	if(l < r) tre[x].ls = build(l,md),tre[x].rs = build(md+1,r);
	return x;
}
int cop(int a,int b,int l,int r,int al,int ar) {
	if(r < al || ar < l || !b) return a;
	if(al >= l && ar <= r) return b;
	tre[++ cnt] = tre[a]; a = cnt;
	int md = (al + ar) >> 1;
	tre[a].ls = cop(tre[a].ls,tre[b].ls,l,r,al,md);
	tre[a].rs = cop(tre[a].rs,tre[b].rs,l,r,md+1,ar);
	tre[a].ct = tre[tre[a].ls].ct + tre[tre[a].rs].ct;
	return a;
}
int findtree(int a,int l,int r,int al,int ar) {
	if(al > r || ar < l || !a) return 0;
	if(al >= l && ar <= r) return tre[a].ct;
	int md = (al + ar) >> 1;
	return findtree(tre[a].ls,l,r,al,md) + findtree(tre[a].rs,l,r,md+1,ar);
}
int rt[MAXN];
int main() {
	n = read();int Q = read(); m = 0;
	rt[0] = build(1,n);
	while(Q --) {
		int op = read();
		if(op == 1) {
			s = read();o = read();k = read();
			rt[k+1] = cop(rt[k+1],rt[k],s,o,1,n);
			m = max(m,k+1);
		}
		else {
			s = read();o = read();
			int as = 0;
			for(int i = 20;i >= 0;i --) {
				if(as+(1<<i) <= m && findtree(rt[as+(1<<i)],s,o,1,n)) as += (1<<i);
			}
			AIput(as,'\n');
		}
	}
	return 0;
}

更多推荐