• 【bzoj4571】[Scoi2016]美味 主席树


    题目描述

    给出一个长度为n的序列,m询问,每次询问求出[l,r]范围内的每一个数加上x再与b异或能够得到的最大值。

    输入

    第1行,两个整数,n,m,表示菜品数和顾客数。
    第2行,n个整数,a1,a2,...,an,表示每道菜的评价值。
    第3至m+2行,每行4个整数,b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。
    1≤n≤2×10^5,0≤ai,bi,xi<10^5,1≤li≤ri≤n(1≤i≤m);1≤m≤10^5

    输出

    输出 m 行,每行 1 个整数,ymax ,表示该位顾客选择的最美味的菜的美味值。

    样例输入

    4 4
    1 2 3 4
    1 4 1 4
    2 3 2 3
    3 2 3 3
    4 1 2 4

    样例输出

    9
    7
    6
    7


    题解

    主席树

    如果没有+x这个条件显然是可持久化Trie树。

    有了这个条件以后不能直接使用可持久化Trie树,但是按位贪心的这个方法仍然可以使用。

    考虑每一位,把满足高位的情况下,使得该位为1的a+x的范围拿出来,减去x得到a的范围。如果[l,r]中这个范围内有数,则该位为1;否则该位为0。

    这样从高位到地位枚举,每次会将当前区间分成两部分(当前位为0和当前位为1),不断确定区间中数的个数即可出解。

    时间复杂度$O(nlog^2 n)$。

    (做完本题后可以回想一下01Trie树的实质:权值范围为$[0,2^k)$的权值线段树。权值范围使得每次查询都可以直接判断子树大小以及O(1)移动节点指针,使得查询复杂度变为$O(log n)$)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 200010
    #define M 1000000
    #define lson l , mid , ls[x] , ls[y]
    #define rson mid + 1 , r , rs[x] , rs[y]
    using namespace std;
    int ls[N * 30] , rs[N * 30] , si[N * 30] , tot , root[N];
    void insert(int p , int l , int r , int x , int &y)
    {
    	y = ++tot , si[y] = si[x] + 1;
    	if(l == r) return;
    	int mid = (l + r) >> 1;
    	if(p <= mid) rs[y] = rs[x] , insert(p , lson);
    	else ls[y] = ls[x] , insert(p , rson);
    }
    int query(int b , int e , int l , int r , int x , int y)
    {
    	if(b <= l && r <= e) return si[y] - si[x];
    	int mid = (l + r) >> 1 , ans = 0;
    	if(b <= mid) ans += query(b , e , lson);
    	if(e > mid) ans += query(b , e , rson);
    	return ans;
    }
    int solve(int b , int x , int l , int r)
    {
    	int i , now = 0 , y , z , ans = 0;
    	for(i = 1 << 17 ; i ; i >>= 1)
    	{
    		if(b & i) y = now , z = now + i - 1;
    		else y = now + i , z = now + 2 * i - 1;
    		y -= x , z -= x;
    		if(query(y , z , -M , M , root[l - 1] , root[r]))
    		{
    			ans |= i;
    			if(!(b & i)) now |= i;
    		}
    		else if(b & i) now |= i;
    	}
    	return ans;
    }
    int main()
    {
    	int n , m , i , b , x , l , r;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &x) , insert(x , -M , M , root[i - 1] , root[i]);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d%d" , &b , &x , &l , &r) , printf("%d
    " , solve(b , x , l , r));
    	return 0;
    }
    
  • 相关阅读:
    hiho #1502:最大子矩阵(元素和不超过k)
    IPC 进程间通信方式——消息队列
    IPC 进程间通信方式——共享内存
    IPC 进程间通信方式——管道
    hiho #1032: 最长回文子串
    TCP超时与重传机制与拥塞避免
    C++关于构造函数 和 析构函数 能否抛出异常的讨论
    基于TCP的客户端、服务器端socket编程
    hiho #1043 : 完全背包
    hiho #1485 : hiho字符串(滑动窗口)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7661015.html
走看看 - 开发者的网上家园