zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    线段拟合(带拉格朗日乘子,HGL)
    工作到位的标准
    Git的简单使用
    位移
    java日期格式化(util包下转成sql包下)
    java中继承的概念
    工作流驳回到指定连线节点上
    年终个人总结
    实现多条件查询 匹配数据库字段中多个数据
    activiti和SSH项目做整合
  • 原文地址:https://www.cnblogs.com/crashed/p/14586027.html
Copyright © 2011-2022 走看看