ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「Ynoi2006」rsrams

2022-06-25 15:33:19  阅读:146  来源: 互联网

标签:sL int rep avai Ynoi2006 MAXN qst 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有