Live2D

Solution -「NOIOL-S 2021」「洛谷 P7470」岛屿探险

\(\mathcal{Description}\)

  Link.

  给定序列 \(\{(a,b)_n\}\)\(q\) 组形如 \((l,r,c,d)\) 的询问,求

\[\Big|\{i\in[l,r]~\big|~a_i\oplus c\le \min\{b_i,d\}\}\Big| \]

  \(n,q\le10^5\)\(a,b,c,d<2^{24}\)

\(\mathcal{Solution}\)

  CDQ 不熟练欸……

  在 \(i\) 的限制条件中,最离谱的应该是 \(\le\min\{b_i,d\}\),再联系部分分,可以想到分别计算两种情况。把原序列当做单点修改,询问离线为前缀查询,发现这就是一个偏序对的贡献问题,外层套一个 CDQ 分治处理。

  接下来,我们需要应对动态插入和询问,分 \((a,b)\) 一定用 \(c\) 查询和 \(a\) 一定用 \((c,d)\) 查询两种情况写两棵不需要持久化的 Trie 树分别维护即可。

  复杂度 \(\mathcal O((n+q)\log(n+q)\log\max\{a,b,c,d\})\)

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>
#include <algorithm>

#define rep( i, l, r ) for ( int i = l, repEnd##i = r; i <= repEnd##i; ++i )
#define per( i, r, l ) for ( int i = r, repEnd##i = l; i >= repEnd##i; --i )

inline int rint() {
	int x = 0, f = 1, s = getchar();
	for ( ; s < '0' || '9' < s; s = getchar() ) f = s == '-' ? -f : f;
	for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
	return x * f;
}

template<typename Tp>
inline void wint( Tp x ) {
	if ( x < 0 ) putchar( '-' ), x = -x;
	if ( 9 < x ) wint( x / 10 );
	putchar( x % 10 ^ '0' );
}

const int MAXN = 1e5, MAXWID = 23;
int n, q, a[MAXN + 5], b[MAXN + 5], qall, ans[MAXN * 2 + 5];
int otmp[MAXN * 3 + 5], ord[MAXN * 3 + 5];

struct Event {
	int r, c, d, id;
	Event(): r( 0 ), c( 0 ), d( 0 ), id( 0 ) {}
	Event( const int r_, const int c_, const int d_, const int id_ ):
		r( r_ ), c( c_ ), d( d_ ), id( id_ ) {}
	inline bool operator < ( const Event& t ) const {
		return r != t.r ? r < t.r : id < t.id;
	}
} evt[MAXN * 3 + 5];

struct DTrie { // maintain a[i] xor c <= d.
	static const int MAXND = 2.3e6;
	int node, root, siz[MAXND + 5], ch[MAXND + 5][2];
	DTrie() { clear(); }
	inline void clear() { node = root = 1, siz[1] = ch[1][0] = ch[1][1] = 0; }
	inline int crtnd() {
		int u = ++node;
		siz[u] = ch[u][0] = ch[u][1] = 0;
		return u;
	}

	inline void insert( int& u, const int x, const int d ) {
		if ( !u ) u = crtnd();
		++siz[u];
		if ( ~d ) insert( ch[u][( x >> d ) & 1], x, d - 1 );
	}

	inline int query( const int u, const int c, const int lim, const int d ) {
		if ( !u ) return 0;
		if ( !~d ) return siz[u];
		int p = c >> d & 1, q = lim >> d & 1, ret = 0;
		if ( q ) return siz[ch[u][p]] + query( ch[u][!p], c, lim, d - 1 );
		else return query( ch[u][p], c, lim, d - 1 );
	} 
} dtr;

struct BTrie { // maintain a[i] xor c <= b[i].
	static const int MAXND = 2.3e6;
	int node, root, tag[MAXND + 5], ch[MAXND + 5][2];
	BTrie() { clear(); }
	inline void clear() { node = root = 1, tag[1] = ch[1][0] = ch[1][1] = 0; }
	inline int crtnd() {
		int u = ++node;
		tag[u] = ch[u][0] = ch[u][1] = 0;
		return u;
	}

	inline void insert( int& u, const int a, const int b, const int d ) {
		if ( !u ) u = crtnd();
		if ( !~d ) return void( ++tag[u] );
		int p = a >> d & 1, q = b >> d & 1;
		if ( q ) {
			if ( !ch[u][p] ) ch[u][p] = crtnd();
			++tag[ch[u][p]];
			insert( ch[u][p ^ 1], a, b, d - 1 );
		} else {
			insert( ch[u][p], a, b, d - 1 );
		}
	}

	inline int query( const int u, const int c, const int d ) {
		if ( !u ) return 0;
		int ret = tag[u];
		if ( !~d ) return ret;
		return ret + query( ch[u][c >> d & 1], c, d - 1 );
	}
} btr;

inline void solve( const int l, const int r ) {
	if ( l >= r ) return ;
	int mid = l + r >> 1, i, j, k;
	solve( l, mid ), solve( mid + 1, r );
	btr.clear(), i = l, j = mid + 1, k = l;
	while ( i <= mid || j <= r ) {
		if ( j > r || ( i <= mid && evt[ord[i]].d <= evt[ord[j]].d ) ) {
			if ( !evt[ord[i]].id ) {
				btr.insert( btr.root, evt[ord[i]].c, evt[ord[i]].d, MAXWID );
			}
			otmp[k++] = ord[i++];
		} else {
			if ( evt[ord[j]].id ) {
				ans[evt[ord[j]].id] +=
					btr.query( btr.root, evt[ord[j]].c, MAXWID );
			}
			otmp[k++] = ord[j++];
		}
	}
	dtr.clear(), i = mid, j = r;
	while ( i >= l || j > mid ) {
		if ( j == mid || ( i >= l && evt[ord[i]].d > evt[ord[j]].d ) ) {
			if ( !evt[ord[i]].id ) {
				dtr.insert( dtr.root, evt[ord[i]].c, MAXWID );
			}
			--i;
		} else {
			if ( evt[ord[j]].id ) {
				ans[evt[ord[j]].id] +=
					dtr.query( dtr.root, evt[ord[j]].c, evt[ord[j]].d, MAXWID);
			}
			--j;
		}
	}
	rep ( i, l, r ) ord[i] = otmp[i];
}

int main() {
	n = rint(), q = rint();
	rep ( i, 1, n ) {
		int a = rint(), b = rint();
		evt[++qall] = Event( i, a, b, 0 );
	}
	rep ( i, 1, q ) {
		int l = rint(), r = rint(), c = rint(), d = rint();
		evt[++qall] = Event( l - 1, c, d, ( i << 1 ) - 1 );
		evt[++qall] = Event( r, c, d, i << 1 );
	}
	rep ( i, 1, qall ) ord[i] = i;
	std::sort( evt + 1, evt + qall + 1 );
	solve( 1, qall );
	rep ( i, 1, q ) wint( ans[i << 1] - ans[( i << 1 ) - 1] ), putchar( '\n' );
	return 0;
}

\(\mathcal{Details}\)

  好久没写这个板块了。

  且不谈考场上能不能解出这道题,给这道题留的思考时间短得明显不合理。一来是策略问题——明明知道难度可能无需,却还骗自己说别人都把 T1 切了然后想了半天;二来,有些不习惯 \(3.5h\) 这种到长不短的比赛时间。

  哎,今后尝试优化考试策略叭。

posted @ 2021-03-27 17:37  Rainybunny  阅读(94)  评论(0编辑  收藏  举报