莫队学习笔记
大抵是太久没碰过了,有点生疏了。
以下仅为本人理解,如有失误还请指出。
可以看看这位大蛇的 juju的莫队练习题
普通莫队
先来知晓一下莫队是干嘛的。
一个经典例题是多次询问区间颜色种类。
如果设计一档询问 \(l_{i-1} <= l_i,r_{i-1} <= r_i\),相信大家都会做吧,直接双指针移动就好了。
可是一般情况我们双指针移动耗时可能较大。
考虑先将询问离线,然后考虑将询问序列排序使得复杂度达到可行速度。
莫队的思想就是控制部分维度,比如让一部分的 \(r_i\) 单调递增,这样 \(R\) 指针的总移动次数就是最多 \(n\) 次了。
考虑平衡一下,假设分 \(B\) 块,那么 \(R\) 移动次数为 \(nB\),\(L\) 移动次数为 \(\frac{n}{B}\times m\),这是因为每个 \(l\) 都在一个块内的话移动次数是不超过 \(\frac{n}{B}\) 次的,当然,这里还漏了一个复杂度,就是每一段第一个要计算出来,复杂度也是 \(nB\) 的。
考虑平衡,\(B\) 取 \(\sqrt{m/2}\) 时理论复杂度最优,但由于上面有些是跑不满的,所以上下浮动也是没错的,那每块长度大概就是 \(\frac{n}{\sqrt{m}}\)。
奇偶性排序
本来这个复杂度已经不错了,不过我们发现就是每一段第一个要计算出来,考虑继续沿用前面的,直接奇数块从前往后,偶数快从后往前,好好利用 \(R\) 指针让它少跑一点。
P3901 数列找不同
#include<bits/stdc++.h>
using namespace std;
int n,q,a[100010],p,l,r,sum,c[100010],t[100010],ml,mr,ans[100010];
struct w
{
int l,r,id,ans,k;
}b[100010];
bool cmp(w x,w y)
{
if(c[x.l] != c[y.l]) return x.l < y.l;
if(c[x.l] & 1) return x.r < y.r;
return x.r > y.r;
}
int main()
{
scanf("%d%d",&n,&q); p = n/sqrt(q);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]),c[i] = (i-1)/p+1;
for(int i = 1;i <= q;i++) scanf("%d%d",&b[i].l,&b[i].r),b[i].id = i;
sort(b + 1,b + 1 + q,cmp);
ml = b[1].r + 1;
mr = b[1].r;
for(int i = 1;i <= q;i++)
{
while(ml < b[i].l)
{
t[a[ml]]--;
if(!t[a[ml]]) sum--;
ml++;
}
while(mr > b[i].r)
{
t[a[mr]]--;
if(!t[a[mr]]) sum--;
mr--;
}
while(mr < b[i].r)
{
mr++;
if(!t[a[mr]]) sum++;
t[a[mr]]++;
}
while(ml > b[i].l)
{
ml--;
if(!t[a[ml]]) sum++;
t[a[ml]]++;
}
if(b[i].r - b[i].l + 1 == sum) ans[b[i].id] = 1;
}
for(int i = 1;i <= q;i++)
{
if(ans[i] == 1) printf("Yes\n");
else printf("No\n");
}
return 0;
}
练习题:P12598 参数要吉祥,考虑用莫队求出每种出现次数有几种数,然后值域分块,此题有一些小细节,为了方便读者自行思考我把细节放这里面了->code,如果你本来就习惯好就不会出错(显然作者习惯不好盯了好一会才恍然大悟)。
P5071 [Ynoi Easy Round 2015] 此时此刻的光辉
写法众多,我随便讲一个。
约数个数等于 \(p_i+1\) 的乘积大家都知道,我们考虑快速求出约数个数,不会 Pollard-rho 怎么办?考虑与处理出 \(\sqrt V\) 以内的质数,然后对于每个数来分解,最后剩下大于 \(1\) 一定也是一个质数,复杂度 \(\sqrt V + \frac{n\sqrt V}{log \sqrt V}\),还可以。
然后考虑莫队,复杂度 \(n\sqrt n log V\),不好,我们考虑根号分治,质数小于等于 \(1000\) 的暴力去跑,复杂度 \(1000 n\),这样最多还有两个质数,直接莫队就好了。
code
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long//取模会快一些
#define getchar() (p1 == p2 && (p2 = (p1 = buf1) + fread(buf1, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf1[1 << 23], *p1 = buf1, *p2 = buf1, ubuf[1 << 23], *u = ubuf;
namespace IO
{
template<typename T>
void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}
template<typename T,typename... Args>
void read(T &_x,Args&...others){Read(_x);Read(others...);}
const int BUF=20000000;char buf[BUF],to,stk[32];int plen;
#define pc(x) buf[plen++]=x
#define flush(); fwrite(buf,1,plen,stdout),plen=0;
template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 1e5+10,B = 32000,B2 = 1000,mod = 19260817;
int n,m,a[N],x,y,z,sum[N<<1],B1,K[N],o;
int v[N],T[N],cnt,T1[N<<1],cnt1,b[N][3],cnt2[N],l,r;
ll inv[N<<1],S,ans[N];
struct w
{
int l,r,id;
}Q[N];
inline bool cmp(w x,w y)
{
if(K[x.l] != K[y.l]) return x.l < y.l;
if((K[x.l]&1)) return x.r < y.r;
return x.r > y.r;
}
inline int ksm(ll x,int p)
{
ll ans = 1;
while(p)
{
if((p&1)) ans = ans*x%mod;
x = x*x%mod,p >>= 1;
}
return ans;
}
inline void add(int x,int z)
{
S = S*inv[sum[x]+1]%mod;
sum[x] += z; S = S*(sum[x]+1)%mod;
}
inline void sol(int x,int z)
{
for(int i = 1;i <= cnt2[x];i++) add(b[x][i],z);
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n),read(m); B1 = n/sqrt(m);
for(int i = 1;i <= n;i++) read(a[i]);
for(int i = 2;i <= B;i++)
if(!v[i])
{
v[i] = i; T[++cnt] = i;
for(int j = i;j <= B;j += i)
v[j] = i;
}
for(int i = 1;i <= m;i++) read(Q[i].l),read(Q[i].r),Q[i].id = i,ans[i] = 1;
o = 1;
while(T[o] <= B2)
{
for(int j = 1;j <= n;j++)
{
sum[j] = sum[j-1];
while(a[j]%T[o] == 0) a[j] /= T[o],sum[j]++;
} o++;
for(int j = 1;j <= m;j++) ans[j] = ans[j]*(sum[Q[j].r]-sum[Q[j].l-1]+1)%mod;
}
for(int i = 1;i <= n;i++)
{
K[i] = (i-1)/B1+1;
for(int j = o;j <= cnt && T[j] <= a[i];j++)
while(a[i]%T[j] == 0) a[i] /= T[j],b[i][++cnt2[i]] = T[j],T1[++cnt1] = T[j];
if(a[i] != 1) T1[++cnt1] = a[i],b[i][++cnt2[i]] = a[i];
}
sort(Q+1,Q+1+m,cmp); S = 1;
sort(T1+1,T1+1+cnt1); cnt1 = unique(T1+1,T1+1+cnt1)-T1-1;
for(int i = 1;i <= cnt1;i++) sum[i] = 0;
for(int i = 1;i <= n*2;i++) inv[i] = ksm(i,mod-2);//直接跑算了,虽然有一定方法(用ksm求i!,i = inv_{i!}*(i-1)!)
for(int i = 1;i <= n;i++)
for(int j = 1;j <= cnt2[i];j++)
b[i][j] = lower_bound(T1+1,T1+1+cnt1,b[i][j])-T1;
l = Q[1].l,r = Q[1].r;
for(int i = l;i <= r;i++) sol(i,1);
for(int i = 1;i <= m;i++)
{
while(Q[i].l < l) l--,sol(l,1);
while(r < Q[i].r) r++,sol(r,1);
while(l < Q[i].l) sol(l,-1),l++;
while(Q[i].r < r) sol(r,-1),r--;
ans[Q[i].id] = ans[Q[i].id]*S%mod;
}
for(int i = 1;i <= m;i++) print(ans[i]),pc('\n');
flush();
return 0;
}
/*
先考虑质因数分解
考虑跑<=sqrt(V)的质数
这样>sqrt(V)的如果无法分解一定是一个质数
这样分解...嗯复杂度nsqrt(n)/log_sqrt(V)还是很快的
然后我们考虑把这些离散化一下,考虑暴力莫队
因为本质上就是(p_1+1)*(p_2+1)*...
先预处理一下逆元,然后直接莫队搞就好了,但是带log...
大空间访问是很慢的,我们考虑一个技巧,<=1000的我们前缀和提前跑了
复杂度m*1000,然后每个数最多还有两个质因子,相当于严格根号了
复杂度n*B/log_B+m*1000+msqrt(n),B = sqrt(V)
*/
带修莫队
普通莫队是不支持修改的,于是有神人继续研究发现了带修莫队。
待修莫队本质上就是多带了一维,所以这个修改必须要方便添加撤销才行。
继续考虑莫队做法,固定 \(Z\) 指针从左往右,另外两维不知道。
复杂度分析跟上面类似,大概是 \(\sqrt[3]{n^2}\times n\),具体分析可以看这里
P1903 [国家集训队] 数颜色 / 维护队列
#include<bits/stdc++.h>
using namespace std;
int n,q,a[200010],p,l,r,sum,t[1000010],ml,mr,ans[200010],o,o1,x,y,k[200010];
char oo;
struct w
{
int l,r,id,z,k;
}b[200010],c[200010];
inline int read() {
int f = 0, t = 0;
char c = getchar();
while (!isdigit(c)) t |= (c == '-'), c = getchar();
while (isdigit(c)) f = (f << 3) + (f << 1) + c - 48, c = getchar();
return t ? -f : f;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar('0' + x % 10);
}
bool cmp(w x,w y)
{
if(k[x.l] == k[y.l])
{
if(k[x.r] == k[y.r]) return x.id < y.id;
return x.r < y.r;
}
return k[x.l] < k[y.l];
}
int main()
{
n = read(),q = read(); p = pow(n,0.666) + 1;
for(int i = 1;i <= n;i++) a[i] = read(),k[i] = (i - 1) / p + 1;
for(int i = 1;i <= q;i++)
{
cin >> oo,x = read(),y = read();
if(oo == 'Q') b[++o].l = x,b[o].r = y,b[o].id = o1,b[o].z = i;
else c[++o1].l = x,c[o1].r = y,c[o1].id = i;
}
sort(b + 1,b + 1 + o,cmp);
ml = b[1].r + 1;
mr = b[1].r; x = 0;
for(int i = 1;i <= o;i++)
{
while(ml < b[i].l)
{
t[a[ml]]--;
if(!t[a[ml]]) sum--;
ml++;
}
while(mr > b[i].r)
{
t[a[mr]]--;
if(!t[a[mr]]) sum--;
mr--;
}
while(mr < b[i].r)
{
mr++;
if(!t[a[mr]]) sum++;
t[a[mr]]++;
}
while(ml > b[i].l)
{
ml--;
if(!t[a[ml]]) sum++;
t[a[ml]]++;
}
while(x < b[i].id)
{
x++;
if(b[i].l <= c[x].l && c[x].l <= b[i].r)
{
t[c[x].r]++,t[a[c[x].l]]--;
if(t[c[x].r] == 1) sum++;
if(!t[a[c[x].l]]) sum--;
}
swap(a[c[x].l],c[x].r);
}
while(x > b[i].id)
{
if(b[i].l <= c[x].l && c[x].l <= b[i].r)
{
t[c[x].r]++,t[a[c[x].l]]--;
if(t[c[x].r] == 1) sum++;
if(!t[a[c[x].l]]) sum--;
}
swap(a[c[x].l],c[x].r);
x--;
}
ans[b[i].z] = sum;
}
for(int i = 1;i <= q;i++) if(ans[i]) write(ans[i]),puts("");
return 0;
}
树上莫队
大概是这个名字吧?
莫队本来是处理序列的,我们知道一棵树有多种序列表达方式,考虑能不能转成序列做。
P4074 [WC2013] 糖果公园
十分厉害的题,本质上是带修树上莫队。
DFS序、时间戳和欧拉序,讲的还不赖,可以复习一下怎么把树转成序列。
具体思路可以看看代码最下面写的,懒得再写一遍了(
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace IO
{
template<typename T>
void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}
template<typename T,typename... Args>
void read(T &_x,Args&...others){Read(_x);Read(others...);}
const int BUF=20000000;char buf[BUF],top,stk[32];int plen;
#define pc(x) buf[plen++]=x
#define flush(); fwrite(buf,1,plen,stdout),plen=0;
template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++top]=48+x%10;while(top) pc(stk[top--]);}
}
using namespace IO;
const int N = 2e5+10;
int B,n,m,q,head[N],cnt,V[N],W[N],v1[N],C[N],K[N],dfn[N],dep[N],las[N],ans[N],cnt1,cnt2,a[N],x,y,z,k,l,r,lg[N],fa[N][20];
int v[N],sum;
struct w
{
int to,nxt;
}b[N<<1];
struct w1
{
int x,y,l,r,lca,id,id1;
}d[N];//询问人从l到r,在进行了id次操作后的答案
struct w2
{
int x,y;
}c[N];//修改操作,将C[x]变为y
inline void add(int x,int y)
{
b[++cnt].nxt = head[x];
b[cnt].to = y;
head[x] = cnt;
}
void dfs(int x,int y)
{
dfn[x] = ++cnt,a[cnt] = x; dep[x] = dep[y]+1,fa[x][0] = y;
for(int i = 1;i <= lg[dep[x]];i++) fa[x][i] = fa[fa[x][i-1]][i-1];
for(int i = head[x];i;i = b[i].nxt)
if(b[i].to != y)
dfs(b[i].to,x);
las[x] = ++cnt,a[cnt] = x;
}
inline int lca(int x,int y)
{
if(dep[x] < dep[y]) swap(x,y);
while(dep[x] != dep[y]) x = fa[x][lg[dep[x]-dep[y]]];
if(x == y) return x;
for(int i = lg[dep[x]];i >= 0;i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i],y = fa[y][i];
return fa[x][0];
}
inline bool cmp(w1 x,w1 y)
{
if(K[x.l] != K[y.l]) return x.l < y.l;
if(K[x.r] != K[y.r]) return x.r < y.r;
return x.id < y.id;
}
inline void sol(int x)
{
v1[x] ^= 1;
if(v1[x] == 1) v[C[x]]++,sum += V[C[x]]*W[v[C[x]]];
else sum -= V[C[x]]*W[v[C[x]]],v[C[x]]--;
}
signed main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
read(n),read(m),read(q); B = pow(2*n,0.666);
for(int i = 1;i <= m;i++) read(V[i]);
for(int i = 1;i <= n;i++) read(W[i]);
for(int i = 1;i <= 2*n;i++) K[i] = (i-1)/B+1;
for(int i = 2;i <= n;i++) lg[i] = lg[i/2]+1;
for(int i = 1;i < n;i++) read(x),read(y),add(x,y),add(y,x);
cnt = 0; dfs(1,0);
for(int i = 1;i <= n;i++) read(C[i]);
for(int i = 1;i <= q;i++)
{
read(x),read(y),read(z);
if(x == 1)
{
d[++cnt1].lca = lca(y,z),d[cnt1].x = y,d[cnt1].y = z,d[cnt1].l = dfn[y],d[cnt1].r = dfn[z],d[cnt1].id = cnt2,d[cnt1].id1 = cnt1;
if(d[cnt1].l > d[cnt1].r) swap(d[cnt1].l,d[cnt1].r),swap(d[cnt1].x,d[cnt1].y);
}
else c[++cnt2].x = y,c[cnt2].y = z;
}
sort(d+1,d+1+cnt1,cmp);
for(int i = 1;i <= d[1].id;i++) swap(C[c[i].x],c[i].y);
z = d[1].id,l = d[1].l,r = d[1].r;
for(int i = d[1].l;i <= d[1].r;i++) sol(a[i]);
for(int i = 1;i <= cnt1;i++)
{
while(l < d[i].l) sol(a[l]),l++;
while(l > d[i].l) l--,sol(a[l]);
while(r < d[i].r) r++,sol(a[r]);
while(r > d[i].r) sol(a[r]),r--;
while(z < d[i].id)
{
z++;
if(l <= dfn[c[z].x] && dfn[c[z].x] <= r) sol(c[z].x);
if(l <= las[c[z].x] && las[c[z].x] <= r) sol(c[z].x);
swap(C[c[z].x],c[z].y);
if(l <= dfn[c[z].x] && dfn[c[z].x] <= r) sol(c[z].x);
if(l <= las[c[z].x] && las[c[z].x] <= r) sol(c[z].x);
}
while(z > d[i].id)
{
if(l <= dfn[c[z].x] && dfn[c[z].x] <= r) sol(c[z].x);
if(l <= las[c[z].x] && las[c[z].x] <= r) sol(c[z].x);
swap(C[c[z].x],c[z].y);
if(l <= dfn[c[z].x] && dfn[c[z].x] <= r) sol(c[z].x);
if(l <= las[c[z].x] && las[c[z].x] <= r) sol(c[z].x);
z--;
}
if(d[i].x != d[i].lca)
{
sol(d[i].x),sol(d[i].lca);
ans[d[i].id1] = sum;
sol(d[i].x),sol(d[i].lca);
}
else ans[d[i].id1] = sum;
}
for(int i = 1;i <= cnt1;i++) print(ans[i]),pc('\n');
flush();
return 0;
}
/*
带修莫队
先考虑把树转成序列,经典的有欧拉序和dfs序
先考虑dfs序,因为每个点恰好出现两次,这样可以理解为第一次进入加,第二次减,用一个桶异或就好了
若询问的两个点有一个是它们的lca,直接dfn[lca]到dfn[x+y-lca]都跑一下就好了
这样的话其它子树的点都会跑两次或没跑,就消了
这个很显然,可以理解为我目前在x了,访问儿子如果访问错了就一直走知道这里删除掉
否则访问对了就不去其它管未访问的x的儿子了,所以没问题。
但凡学了其它算法就会发现,x,y中没有lca多半会有点问题
发现若x,y中没有lca,考虑从x走到y,发现恰好漏掉了x和lca,加上就好了
这个到很好证明,因为lca包含了他们两个,而便利完他们两个后就停止了,所以缺少了lca
然后就是左端点因为访问了两次所以漏了,而它到lca的点因为比它先经过所以只会访问一次,所以就它漏了
所以一共就漏了这两个
考虑如果转成序列怎么做,左右指针移动
操作1(x):若新加的为x,则v[x]++,加上V_x*W_v[x]
操作2(x):若新减的为x,则减去V_x*W_v[x],然后v[x]--
至于遇到修改操作,先看修改的点是否加入了
1.加入了,就减去原来的贡献,然后加上新的贡献
2.未加入,直接更改那个点的值就行了
*/
树上莫队练习题:
SP10707 COT2 - Count on a tree II
P12001 在小小的奶龙山里面挖呀挖呀挖 很有趣的题,可以树上莫队直接做。
P12003 在小小的奶龙山里面挖呀挖呀挖(加强版) 需要根号分治,根据自身喜好写一个其它算法。
回滚莫队/不删除莫队
本质上已不是莫队?
考虑有一些题我们在莫队的时候,存在加入复杂度高或删除复杂度高,但另外一个确在可接受复杂度范围内的情况,这时候我们考虑使用回顾莫队。
具体来讲,我们考虑分块,对于每个 \(r\) 端点在同一个块里面的,设这个块是 \(L,R\),我们考虑将其按 \(l\) 从大到小排序,然后如果 \(l,r\) 在同一块,直接暴力跑,复杂度 \(mB\),否则从大到小扫描线,然后暴力求出 \(L\) 到 \(r\) 的,复杂度分别是 \(\frac{n}{B}\times n + mB\)。
P5906 【模板】回滚莫队&不删除莫队
考虑这个加入很好维护,我们按上诉跑,加入也就是维护每种颜色的最小值和最大值,然后更新即可。
对于 \(l,r\) 在同一块直接暴力跑,否则扫描线那一块记为 \(mx\),然后 \(L\) 到 \(r\) 的记为 \(mx1\),然后我们在跑 \(L\) 到 \(r\) 的顺便看一下右边与左边匹配的最大值,也就是只更新答案,不加入点,然后就没什么了。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define getchar() (p1 == p2 && (p2 = (p1 = buf1) + fread(buf1, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf1[1 << 23], *p1 = buf1, *p2 = buf1, ubuf[1 << 23], *u = ubuf;
namespace IO
{
template<typename T>
void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}
template<typename T,typename... Args>
void read(T &_x,Args&...others){Read(_x);Read(others...);}
const int BUF=20000000;char buf[BUF],to,stk[32];int plen;
#define pc(x) buf[plen++]=x
#define flush(); fwrite(buf,1,plen,stdout),plen=0;
template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 2e5+10,inf = 1e14;
int n,m,ans[N],B,K[N],l,r,o,o1,col[N],L[N],R[N],L1[N],R1[N],T[N],cnt,sum,sum1;
struct w
{
int l,r,id;
}a[N];
inline bool cmp(w x,w y)
{
if(K[x.r] != K[y.r]) return x.r < y.r;
return x.l > y.l;
}
inline void add(int x,int y)
{
L[x] = min(L[x],y);
R[x] = max(R[x],y);
sum = max(sum,R[x]-L[x]);
}
inline void Q(int x,int y){ if(L[x] != inf) sum1 = max(sum1,y-L[x]); }
inline void add1(int x,int y)
{
L1[x] = min(L1[x],y);
R1[x] = max(R1[x],y);
sum1 = max(sum1,R1[x]-L1[x]);
}
inline void del1(int x){ L1[x] = inf,R1[x] = -inf; }
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n); B = sqrt(n); B = max(1ll,B);
for(int i = 1;i <= n;i++) read(col[i]),T[i] = col[i];
sort(T+1,T+1+n); cnt = unique(T+1,T+1+n)-T-1;
for(int i = 1;i <= n;i++) col[i] = lower_bound(T+1,T+1+cnt,col[i])-T;
read(m);
for(int i = 1;i <= m;i++) read(a[i].l),read(a[i].r),a[i].id = i;
for(int i = 1;i <= n;i++) K[i] = (i-1)/B+1;
sort(a+1,a+1+m,cmp); l = 1;
for(int j = 1;j <= cnt;j++) L1[j] = inf,R1[j] = -inf;
for(int i = 1;i <= n;i += B)
{
r = i+B-1;
for(int j = 1;j <= cnt;j++) L[j] = inf,R[j] = -inf; sum = 0; o = i-1;
while(l <= m && a[l].r <= r)
{
sum1 = 0;
while(a[l].l <= o) add(col[o],o),o--;
for(int j = max(i,a[l].l);j <= a[l].r;j++) add1(col[j],j),Q(col[j],j);
for(int j = max(i,a[l].l);j <= a[l].r;j++) del1(col[j]);
ans[a[l].id] = max(sum,sum1);
l++;
}
}
for(int i = 1;i <= m;i++) print(ans[i]),pc('\n');
flush();
return 0;
}
/*
wow,回滚莫队
这个题,显然删除操作很难做,怎么办捏
考虑分块,我们对于右端点r来考虑,考虑所有r在块i里面的询问
将其按左端点从大到小排序然后加入
然后右边的话直接暴力维护(即算那边块内的加上右边和左边的)
感觉已经不太像莫队了(
*/
浙公网安备 33010602011771号