莫队
莫队
普通莫队
假设
实现方法为:离线后排序 顺序处理每个询问 暴力从上一个区间的答案转移到下一个区间的答案(一步一步移动即可)
排序策略:先按照询问的左端点所在的块排序 再按照询问右端点排序
P2709 小B的询问
非常
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 5e4 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int block , ans[N] , res , b[N] , n , m , k , col[N];
struct node { int l , r , id , pos; } a[N];
void add ( int x )
{
res += 2 * b[x] + 1;
b[x] ++;
}
void del ( int x )
{
res -= 2 * b[x] - 1;
b[x] --;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , k = read();
block = sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) col[i] = read();
for ( int i = 1 ; i <= m ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].id = i , a[i].pos = ( a[i].l - 1 ) / block + 1;
sort ( a + 1 , a + m + 1 , [](const node &a , const node &b) { return a.pos == b.pos ? a.r < b.r : a.pos < b.pos; } );\
int l = 1 , r = 0;
for ( int i = 1 ; i <= n ; i ++ )
{
while ( l > a[i].l ) add ( col[--l] );
while ( r < a[i].r ) add ( col[++r] );
while ( l < a[i].l ) del ( col[l++] );
while ( r > a[i].r ) del ( col[r--] );
ans[a[i].id] = res;
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
return 0;
}
P1494 [国家集训队] 小 Z 的袜子
对于
化简 上下同除
那么我们只需要求一个区间内每一种颜色数目的平方和即可
注意
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e4 + 10;
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
inline int gcd ( int a , int b )
{
if ( a % b == 0 ) return b;
return gcd ( b , a % b );
}
int n , m , block , b[N] , res , col[N];
struct node { int l , r , id , pos; } a[N];
struct anss { int a , b; } ans[N];
inline void add ( int x )
{
res += b[x] * 2 + 1;
++ b[x];
}
inline void del ( int x )
{
res -= b[x] * 2 - 1;
-- b[x];
}
signed main ()
{
// ios::sync_with_stdio(false);
// cin.tie(0) , cout.tie(0);
n = read() , m = read(); block = (int)sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) col[i] = read();
for ( int i = 1 ; i <= m ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].id = i , a[i].pos = ( a[i].l - 1 ) / block + 1;
sort ( a + 1 , a + m + 1 , [](const node &a , const node &b) { return a.pos == b.pos ? a.r > b.r : a.pos < b.pos; } );
int l = 1 , r = 0;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( a[i].l < l ) add ( col[--l] );
while ( r < a[i].r ) add ( col[++r] );
while ( l < a[i].l ) del ( col[l++] );
while ( a[i].r < r ) del ( col[r--] );
if ( a[i].l == a[i].r ) { ans[a[i].id] = { 0 , 1 }; continue; }
int tempa = res - ( a[i].r - a[i].l + 1 );
int tempb = ( a[i].r - a[i].l + 1 ) * ( a[i].r - a[i].l );
int tt = gcd ( tempa , tempb );
ans[a[i].id] = { tempa / tt , tempb / tt };
}
for ( int i = 1 ; i <= m ; i ++ ) printf ( "%lld/%lld\n" , ans[i].a , ans[i].b );
return 0;
}
P1972 [SDOI2009] HH的项链
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 1e6 + 10;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
inline void wr(int x){if (x<0) {putchar('-');wr(-x);return;}if(x>=10)wr(x/10);putchar(x%10+'0');}
inline void wrn(int x){wr(x);putchar('\n');}inline void wri(int x){wr(x);putchar(' ');}
int n , m , block , b[N] , res , col[N] , ans[N];
struct node { int l , r , id , pos; } a[N];
inline void add ( int x ) { res += ( ++b[x] == 1 ); }
inline void del ( int x ) { res -= ( --b[x] == 0 ); }
signed main ()
{
n = read();
for ( int i = 1 ; i <= n ; i ++ ) col[i] = read();
m = read();
block = n / sqrt(2*m/3);
for ( int i = 1 ; i <= m ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].id = i , a[i].pos = ( a[i].l - 1 ) / block + 1;
sort ( a + 1 , a + m + 1 , [](const node &a , const node &b) { return a.pos==b.pos?(a.pos&1)?a.r<b.r:a.r>b.r:a.pos<b.pos;} );
int l = 1 , r = 0;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( a[i].l < l ) add ( col[--l] );
while ( r < a[i].r ) add ( col[++r] );
while ( l < a[i].l ) del ( col[l++] );
while ( a[i].r < r ) del ( col[r--] );
ans[a[i].id] = res;
}
for ( int i = 1 ; i <= m ; i ++ ) wrn(ans[i]);
return 0;
}
P4462 [CQOI2018] 异或序列
还是经典的莫队操作 先将询问排序
有一个性质:如果
所以我们考虑维护
那么我们对于
此时对于答案的贡献就是
因为我们维护的是
下面给出两种写法
第一种:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int N = 1e6 + 5;
char buf[1<<22] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , k , block , a[N] , b[N] , res , ans[N];
struct DQY { int l , r , id , pos; } q[N];
void upd ( int x ) { b[a[x]] ++ , res += b[a[x]^k]; }
void del ( int x ) { res -= b[a[x]^k] , b[a[x]] --; }
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , k = read() , block = sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , a[i] ^= a[i-1];
for ( int i = 1 ; i <= m ; i ++ ) q[i].l = read() , q[i].r = read() , q[i].id = i , q[i].pos = ( q[i].l - 1 ) / block + 1;
sort ( q + 1 , q + m + 1 , [](const DQY &a , const DQY &b) { return a.pos == b.pos ? ( ( a.pos & 1 ) ? a.r < b.r : a.r > b.r ) : a.pos < b.pos; } );
int l = 1 , r = 0; b[0] = 1;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( q[i].l < l ) -- l , upd(l-1);
while ( r < q[i].r ) upd(++r);
while ( l < q[i].l ) del(l-1) , l ++;
while ( q[i].r < r ) del(r--);
ans[q[i].id] = res;
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
return 0;
}
也可以是下面一种实现方式:
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int N = 1e6 + 5;
char buf[1<<22] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , k , block , a[N] , b[N] , res , ans[N];
struct DQY { int l , r , id , pos; } q[N];
void updl ( int x ) { b[a[x-1]] ++ , res += b[a[x-1]^k]; }
void updr ( int x ) { b[a[x]] ++ , res += b[a[x]^k]; }
void dell ( int x ) { res -= b[a[x-1]^k] , b[a[x-1]] --; }
void delr ( int x ) { res -= b[a[x]^k] , b[a[x]] --; }
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , k = read() , block = sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , a[i] ^= a[i-1];
for ( int i = 1 ; i <= m ; i ++ ) q[i].l = read() , q[i].r = read() , q[i].id = i , q[i].pos = ( q[i].l - 1 ) / block + 1;
sort ( q + 1 , q + m + 1 , [](const DQY &a , const DQY &b) { return a.pos == b.pos ? ( ( a.pos & 1 ) ? a.r < b.r : a.r > b.r ) : a.pos < b.pos; } );
int l = 1 , r = 0; b[0] = 1;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( q[i].l < l ) updl(--l);
while ( r < q[i].r ) updr(++r);
while ( l < q[i].l ) dell(l++);
while ( q[i].r < r ) delr(r--);
ans[q[i].id] = res;
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
return 0;
}
P4396 [AHOI2013] 作业
莫队+分块
一道很好的综合题
维护两个树状数组 分别为第一个和第二个问题的答案 莫队查询即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int N = 1e6 + 5;
//char buf[1<<22] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m;
int block , a[N] , b[N];
struct query { int l , r , a , b , id , pos; } q[N];
struct DQY { int ans1 , ans2; } ans[N];//你是我的答案=v=
struct bit
{
int t[N];
inline int lowbit ( int x ) { return x & (-x); }
void upd ( int x , int val ) { for ( ; x <= n ; x += lowbit(x) ) t[x] += val; }
int query ( int x ) { int res = 0; for ( ; x ; x -= lowbit(x) ) res += t[x]; return res; }
}bit1 , bit2;
void upd ( int x )
{
bit1.upd ( a[x] , 1 );
if ( ++ b[a[x]] == 1 ) bit2.upd ( a[x] , 1 );
}
void del ( int x )
{
bit1.upd ( a[x] , -1 );
if ( -- b[a[x]] == 0 ) bit2.upd ( a[x] , -1 );
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read() , block = sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 ; i <= m ; i ++ ) q[i].l = read() , q[i].r = read() , q[i].a = read() , q[i].b = read() , q[i].id = i , q[i].pos = ( ( q[i].l - 1 ) / block + 1 );
sort ( q + 1 , q + m + 1 , [](const query &a , const query &b) { return a.pos == b.pos ? a.r < b.r : a.pos < b.pos; } );
int l = 1 , r = 0;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( q[i].l < l ) upd(--l);
while ( r < q[i].r ) upd(++r);
while ( l < q[i].l ) del(l++);
while ( q[i].r < r ) del(r--);
ans[q[i].id].ans1 = bit1.query ( q[i].b ) - bit1.query ( q[i].a - 1 );
ans[q[i].id].ans2 = bit2.query ( q[i].b ) - bit2.query ( q[i].a - 1 );
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i].ans1 << ' ' << ans[i].ans2 << endl;
return 0;
}
同上 将树状数组修改
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int N = 1e5 + 5;
//char buf[1<<22] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m;
int block , ll[N] , rr[N] , a[N] , bel[N] , a1[N] , a2[N] , sum1[N] , sum2[N];
struct query { int l , r , a , b , id , pos; } q[N];
struct DQY { int ans1 , ans2; } ans[N];//你是我的答案=v=
void upd ( int x )
{
a1[a[x]] ++ , sum1[bel[a[x]]] ++;
if ( a1[a[x]] == 1 ) a2[a[x]] ++ , sum2[bel[a[x]]] ++;
}
void del ( int x )
{
a1[a[x]] -- , sum1[bel[a[x]]] --;
if ( a1[a[x]] == 0 ) a2[a[x]] -- , sum2[bel[a[x]]] --;
}
void getans ( int x , int y , int k )
{
int xx = bel[x] , yy = bel[y];
if ( xx == yy ) { for ( int i = x ; i <= y ; i ++ ) ans[k].ans1 += a1[i] , ans[k].ans2 += a2[i]; return; }
for ( int i = xx + 1 ; i <= yy - 1 ; i ++ ) ans[k].ans1 += sum1[i] , ans[k].ans2 += sum2[i];
for ( int i = x ; i <= rr[xx] ; i ++ ) ans[k].ans1 += a1[i] , ans[k].ans2 += a2[i];
for ( int i = ll[yy] ; i <= y ; i ++ ) ans[k].ans1 += a1[i] , ans[k].ans2 += a2[i];
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read() , block = sqrt(n);
int tot = n / block;
if ( n % block ) tot ++;
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , bel[i] = ( i - 1 ) / block + 1;
for ( int i = 1 ; i <= m ; i ++ ) q[i].l = read() , q[i].r = read() , q[i].a = read() , q[i].b = read() , q[i].id = i , q[i].pos = bel[q[i].l];
for ( int i = 1 ; i <= tot ; i ++ ) ll[i] = ( i - 1 ) * block + 1 , rr[i] = i * block;
sort ( q + 1 , q + m + 1 , [](const query &a , const query &b) { return a.pos == b.pos ? ( (a.pos&1) ? a.r < b.r : a.r > b.r ) : a.pos < b.pos; } );
int l = 1 , r = 0;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( q[i].l < l ) upd(--l);
while ( r < q[i].r ) upd(++r);
while ( l < q[i].l ) del(l++);
while ( q[i].r < r ) del(r--);
getans ( q[i].a , q[i].b , q[i].id );
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i].ans1 << ' ' << ans[i].ans2 << endl;
return 0;
}
带修莫队
P1903 [国家集训队] 数颜色 / 维护队列
a[cnta].id = ++cnta , a[cnta].l = read() , a[cnta].r = read() , a[cnta].t = cntq;
a[++cnta].id = cnta , a[cnta].l = read() , a[cnta].r = read() , a[cnta].t = cntq;
乍一看没什么区别 但是我们可以看到前一个代码事正确的 而后面的代码错误
因为赋值号是先从右面的表达式开始计算 所以如果要实现读入询问和
所以最正确的代码是:(上面这两行代码都不推荐采用 不知道编译器什么时候会卡你)
++cnta , a[cnta].id = cnta , a[cnta].l = read() , a[cnta].r = read() , a[cnta].t = cntq;
进入正题 带修莫队是在普通莫队基础上加了一维时间维度来实现的
我们先读入数据 排序策略为:先按照
在修改的时候 我们也要比普通莫队多一次操作: 根据时间进行
也就是在当前区间的
这里有一个小优化:我们对于每一次修改操作 假设这次修改操作是将
所以我们每次将修改操作的
对于多次使用的操作 内联函数要多加
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
constexpr int N = 3e6 + 5;
//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , block;
int cntc , cntq , a[N] , b[N] , ans[N] , bel[N] , res;
struct DQY { int l , r , id , t; } q[N];//你是我的答案=v=
struct change { int p , c; } c[N];
inline void add ( int x ) { res += ( ++ b[x] == 1 ); }
inline void del ( int x ) { res -= ( -- b[x] == 0 ); }
inline void upd ( int x , int t ) { if ( q[x].l <= c[t].p && c[t].p <= q[x].r ) add ( c[t].c ) , del ( a[c[t].p] ); swap ( c[t].c , a[c[t].p] ); }
char ch;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read() , block = pow ( n , 0.666 );
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , bel[i] = ( i - 1 ) / block + 1;
for ( int i = 1 ; i <= m ; i ++ )
{
cin >> ch;
if ( ch == 'Q' ) q[cntq].id = ++cntq , q[cntq].l = read() , q[cntq].r = read() , q[cntq].t = cntc;//t记录的是前面有几个修改操作
else c[++cntc].p = read() , c[cntc].c = read();
}
sort ( q + 1 , q + cntq + 1 , [](const DQY &a , const DQY &b) { return bel[a.l] == bel[b.l] ? ( bel[a.r] == bel[b.r] ? a.t < b.t : bel[a.r] < bel[b.r] ) : bel[a.l] < bel[b.l]; } );
int l = 1 , r = 0 , t = 0;
for ( int i = 1 ; i <= cntq ; i ++ )
{
while ( q[i].l < l ) add(a[--l]);
while ( r < q[i].r ) add(a[++r]);
while ( l < q[i].l ) del(a[l++]);
while ( q[i].r < r ) del(a[r--]);
while ( t < q[i].t ) upd(i,++t);
while ( q[i].t < t ) upd(i,t--);
ans[q[i].id] = res;
}
for ( int i = 1 ; i <= cntq ; i ++ ) cout << ans[i] << endl;
return 0;
}
优化莫队:
P3674 小清新人渣的本愿
首先 我们对于
对于
对于
对于
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int maxn = 1e5;
constexpr int N = 1e5 + 5;
char buf[1<<22] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m;
int block , a[N] , b[N] , ans[N];
bitset<N> now1 , now2;
struct query { int op , l , r , id , x , pos; } q[N];
void upd ( int x ) { if ( ++ b[x] == 1 ) now1[x] = 1 , now2[maxn-x] = 1; }
void del ( int x ) { if ( -- b[x] == 0 ) now1[x] = 0 , now2[maxn-x] = 0; }
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read() , block = sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 ; i <= m ; i ++ ) q[i].op = read() , q[i].l = read() , q[i].r = read() , q[i].x = read() , q[i].id = i , q[i].pos = ( q[i].l - 1 ) / block + 1;
sort ( q + 1 , q + m + 1 , [](const query &a , const query &b) { return a.pos == b.pos ? a.r < b.r : a.pos < b.pos; } );
int l = 1 , r = 0;
for ( int i = 1 ; i <= m ; i ++ )
{
while ( q[i].l < l ) upd(a[--l]);
while ( r < q[i].r ) upd(a[++r]);
while ( l < q[i].l ) del(a[l++]);
while ( q[i].r < r ) del(a[r--]);
int x = q[i].x;
if ( q[i].op == 1 ) if ( ( now1 & ( now1 >> x ) ).any() ) ans[q[i].id] = 1;
if ( q[i].op == 2 ) if ( ( now1 & ( now2 >> maxn - x ) ).any() ) ans[q[i].id] = 1;
if ( q[i].op == 3 )
{
for ( int j = 1 ; j * j <= x ; j ++ )
if ( ! ( x % j ) && now1[x/j] && now1[j] ) { ans[q[i].id] = 1; break; }
}
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ( ans[i] ? "hana" : "bi" ) << endl;
return 0;
}
P4688 [Ynoi2016] 掉进兔子洞
答案显然是
前面的部分很好办 在输入的时候统计即可 现在考虑后面
个人理解是无法做到排序使得三个询问区间绑在一起处理 所以三个区间要分开处理 最后再合并
离散化
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int maxn = 1e5;
constexpr int N = 1e5 + 5;
constexpr int T = 25000;
char buf[1<<22] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , M , block;
int cnt , a[N] , b[N] , ans[N] , bel[N];
bitset<N> f[T] , res;
vector<int> lsh;
struct DQY { int l , r , id; } q[N];//你是我的答案=v=
void upd ( int x ) { res.set ( x + b[x] ) , ++b[x]; }
void del ( int x ) { --b[x] , res.reset ( x + b[x] ); }
void solve ( int m )
{
cnt = 0 , memset ( b , 0 , sizeof b ) , res.reset();
for ( int i = 1 ; i <= m ; i ++ )
{
f[i].set() , ans[i] = 0;
for ( int j = 1 ; j <= 3 ; j ++ )
cnt ++ , q[cnt].l = read() , q[cnt].r = read() , q[cnt].id = i , ans[i] += q[cnt].r - q[cnt].l + 1;
}
sort ( q + 1 , q + cnt + 1 , [](const DQY &a , const DQY &b) { return bel[a.l] == bel[b.l] ? ( bel[a.l] & 1 ? a.r > b.r : a.r < b.r ) : bel[a.l] < bel[b.l]; } );
int l = 1 , r = 0;
for ( int i = 1 ; i <= cnt ; i ++ )
{
while ( q[i].l < l ) upd(a[--l]);
while ( r < q[i].r ) upd(a[++r]);
while ( l < q[i].l ) del(a[l++]);
while ( q[i].r < r ) del(a[r--]);
f[q[i].id] &= res;
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] - f[i].count() * 3 << endl;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , M = read() , block = sqrt(n);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , bel[i] = ( i - 1 ) / block + 1 , lsh.push_back(a[i]);
sort ( lsh.begin() , lsh.end() );
for ( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound ( lsh.begin() , lsh.end() , a[i] ) - lsh.begin() + 1;
for ( int i = 1 ; i <= M ; i += T ) solve ( min ( i + T - 1 , M ) - i + 1 );
return 0;
}
回滚莫队
主要适用范围为:
- 加点操作易实现 删除操作难以实现(例如维护一个集合内的最大值)
- 删除操作易实现 加点操作难以实现(例如维护一个集合内最小未出现的自然数)
歴史の研究
隆重介绍这样一道写了一个上午+半个下午的题 三次更改数组离散化和
对于每一个暴力的块(右端点在左块内) 需要单独开一个桶来记录其中信息 不能和其他块(右端点在左块外)混为一谈
怄火 恼怒 转圈 破防了 为什么我这么菜啊
这道题是上面的第一类问题 对于加点操作直接操作即可 但是对于删除操作 我们删除之后难以知道次大值的值
我们还是按照普通莫队对询问排序 左端点的块单调递增 左端点相同的 右端点单调递增
那么对于询问 有两种情况:
-
右端点在左块内
这时候我们可以直接暴力求 因为块长最多
-
右端点在左块外
根据我们的排序策略 右块内的部分是持续增加的 真正涉及删除操作的是左端点
那么我们对于一些左端点在同一个块内的询问 先将
然后对于一个区间 进行如下操作:
- 先扩展右端点并记录一下扩展后的答案
- 从
指针位置开始向左扩展左端点 为 数组赋值 - 再复原回扩展左端点前的答案 以便下一次扩展左端点
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define print(x) cout << #x << '=' << x << endl
constexpr int N = 3e6 + 5;
constexpr int inf = 0x3f3f3f3f3f3f3f3f;
//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , block , tot;
int a[N] , b[N] , bb[N] , ll[N] , rr[N] , ans[N] , bel[N] , res , lstblock;
struct DQY { int l , r , id; } q[N];//你是我的答案=v=
vector<int>lsh;
inline void add ( int x ) { b[x] ++ , res = max ( res , b[x] * lsh[x] ); }
inline void del ( int x ) { b[x] --; }
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read();
lsh.push_back(-inf);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , lsh.push_back(a[i]);
sort ( lsh.begin() , lsh.end() );
lsh.erase ( unique ( lsh.begin() , lsh.end() ) , lsh.end() );
for ( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound ( lsh.begin() , lsh.end() , a[i] ) - lsh.begin();
block = pow ( n , 0.5 ) , tot = n / block;
if ( n % block ) tot ++;
for ( int i = 1 ; i <= n ; i ++ ) bel[i] = ( i - 1 ) / block + 1;
for ( int i = 1 ; i <= tot ; i ++ ) ll[i] = ( i - 1 ) * block + 1 , rr[i] = i * block;
rr[tot] = n;
for ( int i = 1 ; i <= m ; i ++ ) q[i].l = read() , q[i].r = read() , q[i].id = i;
sort ( q + 1 , q + m + 1 , [](const DQY &a , const DQY &b) { return bel[a.l] == bel[b.l] ? a.r < b.r : bel[a.l] < bel[b.l]; } );
int l = 1 , r = 0;
for ( int i = 1 ; i <= m ; i ++ )
{
if ( bel[q[i].l] == bel[q[i].r] )//必须用一个不同的"bb"数组来存储暴力统计的答案!
{
int temp = 0;
for ( int j = q[i].l ; j <= q[i].r ; j ++ ) bb[a[j]] ++;
for ( int j = q[i].l ; j <= q[i].r ; j ++ ) temp = max ( temp , bb[a[j]] * lsh[a[j]] );
for ( int j = q[i].l ; j <= q[i].r ; j ++ ) bb[a[j]] --;
ans[q[i].id] = temp;
continue;
}
if ( lstblock ^ bel[q[i].l] )
{
while ( r > rr[bel[q[i].l]] ) del(a[r--]);
while ( l < rr[bel[q[i].l]]+1 ) del(a[l++]);
res = 0 , lstblock = bel[q[i].l];
}
while ( r < q[i].r ) add(a[++r]);
int temp = res , l_ = l;
while ( l_ > q[i].l ) add(a[--l_]);
while ( l_ < l ) del(a[l_++]);
ans[q[i].id] = res;
res = temp;
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
return 0;
}
P4137 Rmq Problem / mex
这道题是回滚莫队的第二种情况 删除操作很易实现 但是加点操作不易实现
类比上一道题 我们可以先将整个序列先加进去 然后进行删点操作
注意此时的排序在左端点所在的块相同的时候需要按照右端点降序排序 以便删除区间
初始值
我们对于第一个块
类比上一道题 进行如下操作:
- 先收缩右端点并记录一下收缩后的答案
- 从
指针位置开始向右收缩左端点 为 数组赋值 - 再复原回收缩左端点前的答案 以便下一次收缩左端点
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
constexpr int N = 3e6 + 5;
//#define int long long
//char buf[1<<22] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , block , tot;
int a[N] , b[N] , bb[N] , ll[N] , rr[N] , ans[N] , bel[N] , res , lstblock;
struct DQY { int l , r , id; } q[N];//你是我的答案=v=
inline void add ( int x ) { if ( x > n + 1 ) return; b[x] ++; }//?
inline void del ( int x ) { if ( x > n + 1 ) return; b[x] --; if ( !b[x] ) res = min ( res , x ); }
signed main ()
{
// freopen ( "P4137_1.in" , "r" , stdin );
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
block = sqrt(n) , tot = n / block;
if ( n % block ) tot ++;
for ( int i = 1 ; i <= n ; i ++ ) bel[i] = ( i - 1 ) / block + 1;
for ( int i = 1 ; i <= tot ; i ++ ) ll[i] = ( i - 1 ) * block + 1 , rr[i] = i * block;
rr[tot] = n;
for ( int i = 1 ; i <= m ; i ++ ) q[i].l = read() , q[i].r = read() , q[i].id = i;
sort ( q + 1 , q + m + 1 , [](const DQY &a , const DQY &b) { return bel[a.l] == bel[b.l] ? a.r > b.r : bel[a.l] < bel[b.l]; } );
for ( int i = 1 ; i <= n ; i ++ ) b[a[i]] ++;
while ( b[res] ) res ++;
//先将所有东西都加入队列中
int st = res;
int l = 1 , r = n;
for ( int i = 1 ; i <= m ; i ++ )
{
if ( bel[q[i].l] == bel[q[i].r] )
{
int tmp = 0;
for ( int j = q[i].l ; j <= q[i].r ; j ++ ) if ( a[j] <= n + 1 ) bb[a[j]] ++;
while ( bb[tmp] ) tmp ++;
for ( int j = q[i].l ; j <= q[i].r ; j ++ ) if ( a[j] <= n + 1 ) bb[a[j]] --;
ans[q[i].id] = tmp;
continue;
}
if ( bel[q[i].l] ^ lstblock )
{
res = st;
while ( r < n ) add(a[++r]);
while ( l < ll[bel[q[i].l]] ) del(a[l++]);
st = res , lstblock = bel[q[i].l];
}
while ( q[i].r < r ) del(a[r--]);
int temp = res , l_ = l;
while ( l_ < q[i].l ) del(a[l_++]);
while ( l_ > l ) add(a[--l_]);
ans[q[i].id] = res;
res = temp;
}
for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
return 0;
}
贴一个主席树做法捏()
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
constexpr int N = 3e6 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get()
int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , a[N] , root[N];
vector<int> lsh;
struct DQY
{
struct node { int son[2] , minn; } t[N<<5];
int tot = 0;
inline int new_node ( int p ) { t[++tot] = t[p]; return tot; }
inline void up ( int p ) { t[p].minn = min ( t[ls(p)].minn , t[rs(p)].minn ); }
void upd ( int &p , int l , int r , int x , int val )
{
p = new_node(p);
if ( l == r ) return t[p].minn = val , void();
if ( x <= mid ) upd ( lson , x , val );
else upd ( rson , x , val );
up(p);
}
int query ( int p , int l , int r , int x )
{
if ( l == r ) return l;
if ( t[ls(p)].minn < x ) return query ( lson , x );
else return query ( rson , x );
}
}T;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
lsh.push_back(0);
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , lsh.push_back(a[i]) , lsh.push_back(a[i]+1);
sort ( lsh.begin() , lsh.end() );
lsh.erase ( unique ( lsh.begin() , lsh.end() ) , lsh.end() );
int sz = lsh.size();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound ( lsh.begin() , lsh.end() , a[i] ) - lsh.begin();
for ( int i = 1 ; i <= n ; i ++ ) root[i] = root[i-1] , T.upd ( root[i] , 0 , sz , a[i] , i );
for ( int i = 1 ; i <= m ; i ++ )
{
int l = read() , r = read();
cout << lsh[T.query ( root[r] , 0 , sz , l )] << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!