其他分享
首页 > 其他分享> > 「Ynoi2006」rsrams

「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\) 之间的关系:

  1. 如果 \(I_c\subseteq I_q\),直接区间逆序对预处理;
  2. 如果 \(I_c\) 和 \(I_q\) 交成一个 \(I_c\) 前缀或者后缀,直接预处理;
  3. 否则,我们需要处理 \(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