「Ynoi2006」rsrams
作者:互联网
题目
点这里看题目。
分析
题目就是区间询问子区间绝对众数和(如果没有,即为 0)。然而,这里并不是以那个经典算法作为切口入手的。
Remark. 所以,某类问题有好的算法并不意味着它一定通用。有时候还是应该回归基础方法。
尝试枚举区间众数,则可以在枚举之后,修改为询问子区间中有多少个区间的和 \(>0\)。这个算法虽然是经典的二离莫队问题,然而如果要做 \(O(mn)\) 个询问显然相当吃力。因此,类似于 「SDOI2022」整数序列的做法,我们实际上只需要对于每种数,处理出那些“存在一个 \(j\),使得区间 \([i,j]\) 或者区间 \([j,i]\) 的和 \(\ge 0\)”的所有 \(i\),且这样的 \(i\) 构成了若干个区间,总长度显然为 \(O(n)\)。
接下来,单独考虑两个区间之间的贡献。假如有一个贡献区间 \(I_c=[l_c,r_c]\) 和一个询问区间 \(I_q=[l_q,r_q]\),我们在 \(I_c\) 中滚一个前缀和 \(s\),那么就相当于询问有多少个 \(\max\{l_c,l_q\}-1\le i< j\le \min\{r_c,r_q\}\),使得 \(s_j-s_i>0\),这是一个典型的区间逆序对问题。但是,由于询问区间零零碎碎很多,我们还需要讨论一下 \(I_c\) 和 \(I_q\) 之间的关系:
- 如果 \(I_c\subseteq I_q\),直接区间逆序对预处理;
- 如果 \(I_c\) 和 \(I_q\) 交成一个 \(I_c\) 前缀或者后缀,直接预处理;
- 否则,我们需要处理 \(I_q\) 的区间逆序对,并且还必须在 \(s\) 的背景下进行;
Remark. 数据结构的一大特点就是尽可能的将每一类情况都优化到最快。如果不会变得很复杂,把不同的情况区分开从而进行更加细致的优化。
个人感觉,这里是这道题的关键之一。如果不作这样的讨论会比较棘手。当然也有可能就是我 Ynoi 做少了。
前两种情况比较散乱,但是我们可以根号分治。对于长度 \(\le \sqrt n\) 的区间,可以枚举每一个子区间(或者子前缀),进而转化成二维数点。这样至多有 \(O(n\sqrt n)\) 个点和 \(O(m)\) 次询问,使用分块平衡一下。对于 \(>\sqrt n\),直接预处理之后暴力枚举计算贡献即可。
好吧,不装了,上面这段是在抄题解。实际上,前两种情况都可以直接做扫描线。我们可以分别处理前缀和后缀两种情况,方法类似:在遇到 \(I_c\) 时,用树状数组计算出贡献并算入序列扫描线,再用另外一个树状数组来维护序列扫描线的信息。
第三种情况比较麻烦。不过,题解里面给出了一个很神奇的结论:每个询问区间被完整地包含了至多 \(O(\log n)\) 次。
考虑长度落在 \([2^k,2^{k+1})\) 的 \(I_c\),它的绝对众数所占位置 \(\ge \frac {|I_c|} 2\) 个,且所有这样的 \(I_c\) 能够覆盖的总长度 \(\le 2^{k+2}\),且 \(I_c\) 对应的绝对众数必然互不相同。因此,可行的绝对众数只有常数个。
本题另一个关键点所在。
Remark. 关键还是在“绝对”二字上,如果不是“绝对”限制了绝对比例,光靠众数的“相对”比例还没法证明这一点。
另一方面,这里选取 \([2^k,2^{k+1})\) 似乎仅仅是构造性地给出了一个便于讨论的框架。如果自己动手,丢弃这样的分组办法来感知也不是不可以。
最后,区间逆序对对于单个大区间可以做到 \(O(n\sqrt q+q+n^{1+\varepsilon})\),最后那一项我还真不知道怎么来的。总复杂度实际上是 \(O(n\sqrt{m\log n}+m\log n+n^{1+\varepsilon})\)。
代码
#include <set>
#include <cmath>
#include <cstdio>
#include <vector>
#include <algorithm>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef long long LL;
const int MAXN = 1e6 + 5, MAXB = 2e3 + 5;
template<typename _T>
void read( _T &x ) {
x = 0; char s = getchar(); int f = 1;
while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
x *= f;
}
template<typename _T>
void write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) write( x / 10 );
putchar( x % 10 + '0' );
}
template<typename _T>
inline _T Min( const _T &a, const _T &b ) {
return a < b ? a : b;
}
struct Range {
int l, r, c;
Range(): l( 0 ), r( 0 ), c( 0 ) {}
Range( int L, int R, int C ): l( L ), r( R ), c( C ) {}
};
struct BIT {
LL a[MAXN << 1]; int n;
inline void Down( int &x ) { x &= x - 1; }
inline void Up( int &x ) { x += x & ( - x ); }
inline void Update( int x, LL v ) { for( ; x <= n ; Up( x ) ) a[x] += v; }
inline void Update( int l, int r, LL v ) { Update( l, v ), Update( r + 1, - v ); }
inline LL Query( int x ) { LL ret = 0; for( ; x ; Down( x ) ) ret += a[x]; return ret; }
inline LL Query( const int &l, const int &r ) { return Query( r ) - Query( l - 1 ); }
inline void Init( const int &N ) { n = N; rep( i, 1, n ) a[i] = 0; }
};
std :: vector<int> atLeft[MAXN], atRight[MAXN], qryLeft[MAXN], qryRight[MAXN];
Range rng[MAXN];
int tot = 0;
LL ans[MAXN];
int qL[MAXN], qR[MAXN];
int A[MAXN];
int N, M;
namespace Division {
std :: vector<int> pos[MAXN], avai;
std :: set<int> rem;
bool inProg[MAXN];
inline void Work() {
rep( i, 1, N ) pos[A[i]].push_back( i );
rep( i, 1, N ) rem.insert( i );
rep( i, 1, N ) if( ! pos[i].empty() ) {
avai.clear();
int n = pos[i].size(), m;
rep( j, 0, n - 1 )
inProg[pos[i][j]] = true,
rem.erase( pos[i][j] );
rep( j, 0, n - 1 ) {
avai.push_back( pos[i][j] );
std :: set<int> :: iterator it = rem.upper_bound( pos[i][j] );
if( it != rem.begin() ) {
avai.push_back( * -- it );
rem.erase( it );
}
}
m = avai.size();
rep( j, 0, m - 1 )
if( ! inProg[avai[j]] )
rem.insert( avai[j] );
per( j, n - 1, 0 ) {
std :: set<int> :: iterator it = rem.upper_bound( pos[i][j] );
if( it != rem.end() ) {
avai.push_back( *it );
rem.erase( it );
}
}
m = avai.size();
rep( j, 0, m - 1 )
rem.insert( avai[j] );
std :: sort( avai.begin(), avai.end() );
avai.erase( avai.end() = std :: unique( avai.begin(), avai.end() ), avai.end() );
m = avai.size();
for( int l = 0, r ; l < m ; l = r ) {
for( r = l + 1 ; r < m && avai[r] - avai[r - 1] == 1 ; r ++ );
rng[++ tot] = ( Range ) { avai[l], avai[r - 1], i };
atLeft[rng[tot].l].push_back( tot );
atRight[rng[tot].r].push_back( tot );
}
rep( j, 0, n - 1 ) inProg[pos[i][j]] = false;
}
}
}
namespace Part12 {
int pref[MAXN];
BIT val, seq;
void Work() {
// Well, I think two parts can be combined into one.
val.Init( N << 1 | 1 ), seq.Init( N );
rep( i, 1, N ) {
int n = atRight[i].size();
rep( j, 0, n - 1 ) {
int l = rng[atRight[i][j]].l,
r = rng[atRight[i][j]].r,
c = rng[atRight[i][j]].c;
pref[l - 1] = 0;
rep( k, l, r ) pref[k] = pref[k - 1] + ( A[k] == c ? +1 : -1 );
val.Update( pref[r] + N + 1, 1 );
per( k, r - 1, l - 1 ) {
seq.Update( k + 1, val.Query( pref[k] + N + 2, N + N + 1 ) * c );
val.Update( pref[k] + N + 1, 1 );
}
rep( k, l - 1, r ) val.Update( pref[k] + N + 1, -1 );
}
n = qryRight[i].size();
rep( j, 0, n - 1 ) {
int cur = qryRight[i][j];
ans[cur] += seq.Query( qL[cur], qR[cur] );
}
}
val.Init( N << 1 | 1 ), seq.Init( N );
per( i, N, 1 ) {
int n = atLeft[i].size();
rep( j, 0, n - 1 ) {
int l = rng[atLeft[i][j]].l,
r = rng[atLeft[i][j]].r,
c = rng[atLeft[i][j]].c;
pref[l - 1] = 0;
rep( k, l, r ) pref[k] = pref[k - 1] + ( A[k] == c ? +1 : -1 );
val.Update( pref[l - 1] + N + 1, 1 );
rep( k, l, r - 1 ) {
seq.Update( k, r - 1, val.Query( 1, pref[k] + N ) * c );
val.Update( pref[k] + N + 1, 1 );
}
rep( k, l - 1, r - 1 ) val.Update( pref[k] + N + 1, -1 );
}
n = qryLeft[i].size();
rep( j, 0, n - 1 ) {
int cur = qryLeft[i][j];
ans[cur] += seq.Query( qL[cur], qR[cur] );
}
}
}
}
namespace Part3 {
int Bq;
struct Question {
int l, r, blk, id;
Question(): l( 0 ), r( 0 ), blk( 0 ), id( 0 ) {}
Question( int L, int R, int I ): l( L ), r( R ), blk( ( L - 1 ) / Bq + 1 ), id( I ) {}
inline bool operator < ( const Question &g ) const {
return blk == g.blk ? ( blk & 1 ? r < g.r : r > g.r ) : blk < g.blk;
}
};
LL su[MAXN << 1], tag[MAXB];
int bel[MAXN << 1];
int lef[MAXB], rig[MAXB];
std :: vector<Range> preQuery[MAXN], sufQuery[MAXN];
LL partRes[MAXN << 1];
BIT val;
Question qst[MAXN];
int q = 0;
LL preRes[MAXN], sufRes[MAXN];
int pref[MAXN];
int n, blk, B;
inline void Build( const int &len ) {
blk = 0, B = sqrt( len );
for( int l = 1 ; l <= len ; l += B ) {
blk ++, lef[blk] = l, rig[blk] = Min( l + B - 1, len );
for( int j = lef[blk] ; j <= rig[blk] ; j ++ )
su[j] = 0, bel[j] = blk;
tag[blk] = 0;
}
}
inline void UpdateSuf( int x, const int &delt ) {
if( x > 2 * n + 1 ) return ;
int b = bel[x];
rep( i, x, rig[b] ) su[i] += delt;
rep( i, b + 1, blk ) tag[i] += delt;
}
inline void UpdatePre( int x, const int &delt ) {
if( x < 1 ) return ;
int b = bel[x];
rep( i, lef[b], x ) su[i] += delt;
rep( i, 1, b - 1 ) tag[i] += delt;
}
inline LL Query( const int &x ) {
return su[x] + tag[bel[x]];
}
void Work() {
rep( i, 1, N )
std :: sort( qryRight[i].begin(), qryRight[i].end(),
[] ( const int &a, const int &b ) -> bool {
return qL[a] < qL[b];
} );
rep( i, 1, tot ) {
int l = rng[i].l, r = rng[i].r, c = rng[i].c;
pref[1] = q = 0, Bq = ceil( sqrt( n = r - l + 2 ) );
rep( j, l, r ) pref[j - l + 2] = pref[j - l + 1] + ( A[j] == c ? +1 : -1 );
rep( j, l, r - 1 ) {
int sL = 0, sR = qryRight[j].size(), mid;
while( sL < sR ) {
mid = ( sL + sR ) >> 1;
if( qL[qryRight[j][mid]] > l ) sR = mid;
else sL = mid + 1;
}
rep( k, sL, ( int ) qryRight[j].size() - 1 ) {
int cur = qryRight[j][k];
qst[++ q] = ( Question ) { qL[cur] - l + 1, qR[cur] - l + 2, cur };
}
}
if( ! q ) continue;
val.Init( n << 1 | 1 );
rep( j, 0, n + 1 ) preQuery[j].clear(), sufQuery[j].clear();
rep( j, 1, n ) {
preRes[j] = val.Query( pref[j] + n );
val.Update( pref[j] + n + 1, 1 );
}
val.Init( n << 1 | 1 );
per( j, n, 1 ) {
sufRes[j] = val.Query( pref[j] + n + 2, n << 1 | 1 );
val.Update( pref[j] + n + 1, 1 );
}
std :: sort( qst + 1, qst + 1 + q );
int sL = 1, sR = 0, id = 0;
rep( j, 1, q ) {
if( sR < qst[j].r ) {
preQuery[sL - 1].push_back( Range( sR + 1, qst[j].r, ++ id ) );
partRes[id] = 0, sR = qst[j].r;
if( sL < qst[j].l ) {
sufQuery[sR + 1].push_back( Range( sL, qst[j].l - 1, ++ id ) );
partRes[id] = 0, sL = qst[j].l;
}
if( sL > qst[j].l ) {
sufQuery[sR + 1].push_back( Range( qst[j].l, sL - 1, ++ id ) );
partRes[id] = 0, sL = qst[j].l;
}
} else {
if( sL < qst[j].l ) {
sufQuery[sR + 1].push_back( Range( sL, qst[j].l - 1, ++ id ) );
partRes[id] = 0, sL = qst[j].l;
}
if( sL > qst[j].l ) {
sufQuery[sR + 1].push_back( Range( qst[j].l, sL - 1, ++ id ) );
partRes[id] = 0, sL = qst[j].l;
}
if( sR != qst[j].r ) {
preQuery[sL - 1].push_back( Range( qst[j].r + 1, sR, ++ id ) );
partRes[id] = 0, sR = qst[j].r;
}
}
}
Build( n << 1 | 1 );
rep( j, 0, n ) {
if( j ) UpdateSuf( pref[j] + n + 2, 1 );
int m = preQuery[j].size();
rep( k, 0, m - 1 ) {
Range cur = preQuery[j][k];
rep( t, cur.l, cur.r )
partRes[cur.c] += preRes[t] - Query( pref[t] + n + 1 );
}
}
Build( n << 1 | 1 );
per( j, n + 1, 1 ) {
if( j <= n ) UpdatePre( pref[j] + n, 1 );
int m = sufQuery[j].size();
rep( k, 0, m - 1 ) {
Range cur = sufQuery[j][k];
rep( t, cur.l, cur.r )
partRes[cur.c] += sufRes[t] - Query( pref[t] + n + 1 );
}
}
sL = 1, sR = 0, id = 0; LL res = 0;
rep( j, 1, q ) {
if( sR < qst[j].r ) {
res += partRes[++ id];
if( sL < qst[j].l ) res -= partRes[++ id];
if( sL > qst[j].l ) res += partRes[++ id];
} else {
if( sL < qst[j].l ) res -= partRes[++ id];
if( sL > qst[j].l ) res += partRes[++ id];
if( sR != qst[j].r ) res -= partRes[++ id];
}
sL = qst[j].l, sR = qst[j].r;
ans[qst[j].id] += res * c;
}
}
}
}
int main() {
read( N ), read( M );
rep( i, 1, N ) read( A[i] );
rep( i, 1, M ) {
read( qL[i] ), read( qR[i] );
qryLeft[qL[i]].push_back( i );
qryRight[qR[i]].push_back( i );
}
Division :: Work();
Part12 :: Work();
Part3 :: Work();
rep( i, 1, M ) write( ans[i] ), putchar( '\n' );
return 0;
}
标签:sL,int,rep,avai,Ynoi2006,MAXN,qst,rsrams 来源: https://www.cnblogs.com/crashed/p/16411664.html