[NOI Online 2021 提高组] 岛屿探险

题目

点这里看题目。

分析

放在 T3 但明明还没有 T1 难。

由于整个问题只有查询,并且各个岛屿都是独立的,所以显然可以拆成前缀进行询问。

前缀的话就比较自由了。考虑已有的一个岛屿集,我们分析一下询问在干什么:

  1. 如果某个岛屿 ((a,b)) 满足 (b>d) ,那么对于所有这样的岛屿, (b) 都已经没用了。我们相当于是求有多少个 (a) 满足 (aoplus cle d) ,这是常规的 0-1Trie 可以解决的问题。

  2. 如果某个岛屿 ((a,b)) 满足 (ble d) ,那么对于所有这样的岛屿, (d) 都已经没用了。我们相当于是求有多少个 (a) 使得 (aoplus cle b) 得到了满足。那么考虑一对 ((a,b)) 会给哪些 (c) 带来贡献。在二进制的第 (k) 位上:

    1. 如果 (b) 为 1 ,此时 (aoplus c) 如果为 0 , (c)(0sim k-1) 位的值就随意了,因此这一部分的 (c) 都可以获得 1 的贡献;如果 (aoplus c) 为 1 ,那么 (c) 就需要进入更低位判断。
    2. 如果 (b) 为 0 ,此时 (aoplus c) 只能为 0 ,这一位就确定了。

    如果同样用 0-1Trie 进行维护,那么可以在情况 1 中,在 (aoplus c) 为 0 的一侧打上标记(可以直接标记永久化)。查询的时候直接顺着 (c) 一路向下查询即可。

再思考一下外层的结构,如果用树套树会获得 MLE 的好成绩,外层使用 CDQ 分治即可省下来这部分空间。

不妨设 (V) 为值域,时间为 (O(nlog_2nlog_2|V|)) ,空间为 (O(nlog_2|V|))

代码

#include <cstdio>
#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 -- )

const int BIT = 23, LIM = ( 1 << 24 ) - 1;
const int MAXN = 1e5 + 5, MAXS = MAXN * 25;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < '0' || '9' < s ) { 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>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

struct Event
{
	int i, b, c, id;
	Event() { i = b = c = id = 0; }
	Event( int I, int B, int C, int ID ) { i = I, b = B, c = C, id = ID; }
	bool operator < ( const Event &g ) const { return i == g.i ? id < g.id : i < g.i; }
}eve[MAXN * 3];

int seq[MAXN * 3], tmp[MAXN * 3], len;

int res[MAXN * 3];
int A[MAXN], B[MAXN];
int N, Q;

namespace Trie
{
	int ch[2][MAXS], su[MAXS];
	int rt, tot;
	
	void Clear() { rt = tot = 0; }
	
	inline int NewNode()
	{
		int r = ++ tot;
		ch[0][r] = ch[1][r] = su[r] = 0;
		return r;
	}
	
	void Insert( int &x, const int k, const int v )
	{
		su[x = x ? x : NewNode()] ++;
		if( k < 0 ) return ;
		Insert( ch[v >> k & 1][x], k - 1, v );
	}
	
	int Query( const int &x, const int k, const int XOR, const int lim )
	{
		if( ! x ) return 0;
		if( k < 0 ) return su[x];
		int dif = XOR >> k & 1;
		if( ! ( lim >> k & 1 ) ) return Query( ch[dif][x], k - 1, XOR, lim );
		return Query( ch[dif ^ 1][x], k - 1, XOR, lim ) + su[ch[dif][x]];
	}
	
	void Insert( const int v ) { Insert( rt, BIT, v ); }
	int Query( const int XOR, const int lim ) { return Query( rt, BIT, XOR, lim ); }
}

namespace NewbieTrie
{
	int ch[2][MAXS << 1], tag[MAXS << 1];
	int rt, tot;
	
	void Clear() { rt = tot = 0; }
	
	inline int NewNode()
	{
		int r = ++ tot;
		ch[0][r] = ch[1][r] = tag[r] = 0;
		return r;
	}
	
	void Add( int &x, const int v ) { tag[x = x ? x : NewNode()] += v; }

	void Insert( int &x, const int k, const int a, const int b )
	{
		x = x ? x : NewNode();
		if( k < 0 ) return void( tag[x] ++ );
		int p = a >> k & 1, q = b >> k & 1;
		if( q == 1 ) 
		{
			Add( ch[p][x], 1 );
			Insert( ch[1 ^ p][x], k - 1, a, b );
		}
		else Insert( ch[p][x], k - 1, a, b );
	}
	
	int Query( const int &x, const int k, const int v )
	{
		if( ! x ) return 0; int ret = tag[x];
		if( ~ k ) ret += Query( ch[v >> k & 1][x], k - 1, v );
		return ret;
	}
	
	int Query( const int v ) { return Query( rt, BIT, v ); }
	void Insert( const int a, const int b ) { Insert( rt, BIT, a, b ); }
}

void CDQ( const int l, const int r )
{
	if( l >= r ) return ;
	int mid = l + r >> 1;
	CDQ( l, mid ), CDQ( mid + 1, r );
	// now seq[l:mid] and seq[mid+1:r] is already sorted by b
	int i = l, j = mid + 1, u, v, k = l - 1; 
	NewbieTrie :: Clear();
	for( ; i <= mid || j <= r ; )
	{
		u = seq[i], v = seq[j];
		if( i <= mid && ( j > r || eve[u].b <= eve[v].b ) )
		{
			if( eve[u].id < 0 )
				NewbieTrie :: Insert( eve[u].c, eve[u].b );
			tmp[++ k] = seq[i ++];
		}
		else
		{
			if( eve[v].id > 0 )
				res[eve[v].id] += NewbieTrie :: Query( eve[v].c );
			tmp[++ k] = seq[j ++];
		}
	}
	// Process to those b <= d
	i = mid, j = r, Trie :: Clear();
	for( ; i >= l || j > mid ; )
	{
		u = seq[i], v = seq[j];
		if( i >= l && ( j <= mid || eve[u].b > eve[v].b ) )
		{
			if( eve[u].id < 0 )
				Trie :: Insert( eve[u].c );
			i --;
		}
		else
		{
			if( eve[v].id > 0 )
				res[eve[v].id] += Trie :: Query( eve[v].c, eve[v].b );
			j --;
		}
	}
	// Process to those b > d
	rep( i, l, r ) seq[i] = tmp[i];
	// Merge!!
}

int main()
{
	read( N ), read( Q );
	rep( i, 1, N )
	{
		read( A[i] ), read( B[i] );
		eve[++ len] = Event( i, B[i], A[i], - i );
	}
	rep( i, 1, Q )
	{
		int l, r, c, d;
		read( l ), read( r ), read( c ), read( d );
		eve[++ len] = Event( l - 1, d, c, i << 1 );
		eve[++ len] = Event( r, d, c, i << 1 | 1 );
	}
	rep( i, 1, len ) seq[i] = i;
	std :: sort( eve + 1, eve + 1 + len );
	CDQ( 1, len );
	rep( i, 1, Q )
		write( res[i << 1 | 1] - res[i << 1] ), putchar( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/14586027.html