题面
🔗 (提交:🔗,洛谷:🔗)
有一个序列 { a n } \{a_n\} {an},初始时全为 0 0 0,有 q q q 次操作:
1 l r x
:对所有 l ≤ i ≤ r l\leq i\leq r l≤i≤r ,若 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,x≤5×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;
}