• BZOJ4517[Scoi2016]美味——主席树


    题目描述

    一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1≤i≤n)。有 m 位顾客,第 i 位顾客的期
    望值为 bi,而他的偏好值为 xi 。因此,第 i 位顾客认为第 j 道菜的美味度为 bi XOR (aj+xi),XOR 表示异或
    运算。第 i 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第 
    li 道到第 ri 道中选择。请你帮助他们找出最美味的菜。
     

    输入

    第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
     
      一看到位异或第一时间就会想到按位从高到底贪心,这道题也是这样的,但稍有不同的是要两个数的和与另一个数位异或结果最大。还是从高位到低位考虑,如果b当前为是1(0的方法一样),那么希望x+a这一位是0,因为更高位已经确定了,所以满足要求的a+x的值一定是一个区间——假设更高位已经确定的答案是ans(ans的值即为更高位贪心取最优解,其他位都是0),那么a+x的区间就是[ans,ans+(1<<i)-1],把x挪到等号那边就能知道要使这一位异或结果最优所需的a的范围,只要在主席树上查询是否有这个区间内的数,有的话把ans的这一位加上b这一位相反的数。因为得到的ans是a+x的值,所以最后还要ans^=b。
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,m;
    int l,r;
    int cnt;
    int ans;
    int maxv;
    int a,b,x;
    int ls[4000010];
    int rs[4000010];
    int sum[4000010];
    int root[200010];
    int updata(int pre,int l,int r,int v)
    {
        int rt=++cnt;
        if(l==r)
        {
            sum[rt]=sum[pre]+1;
            return rt;
        }
        ls[rt]=ls[pre];
        rs[rt]=rs[pre];
        sum[rt]=sum[pre]+1;
        int mid=(l+r)>>1;
        if(v<=mid)
        {
            ls[rt]=updata(ls[pre],l,mid,v);
        }
        else
        {
            rs[rt]=updata(rs[pre],mid+1,r,v);
        }
        return rt;
    }
    int query(int x,int y,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)
        {
            return sum[y]-sum[x];
        }
        int mid=(l+r)>>1;
        if(L>mid)
        {
            return query(rs[x],rs[y],mid+1,r,L,R);
        }
        else if(R<=mid)
        {
            return query(ls[x],ls[y],l,mid,L,R);
        }
        else
        {
            return query(ls[x],ls[y],l,mid,L,R)+query(rs[x],rs[y],mid+1,r,L,R);
        }
    }
    bool find(int l,int r,int L,int R)
    {
        L=max(0,L);
        R=min(maxv,R);
        if(L>R)
        {
            return 0;
        }
        else
        {
            return query(root[l],root[r],0,1<<18,L,R);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a);
            maxv=max(a,maxv);
            root[i]=updata(root[i-1],0,1<<18,a);
        }
        for(int j=1;j<=m;j++)
        {
            scanf("%d%d%d%d",&b,&x,&l,&r);
            ans=0;
            for(int i=17;i>=0;i--)
            {
                int now=ans+((((b>>i)&1)^1)<<i);
                if(find(l-1,r,now-x,now+(1<<i)-1-x))
                {
                    ans=now;
                }
                else
                {
                    ans+=(((b>>i)&1)<<i);
                }
            }
            printf("%d
    ",ans^b);
        }
    }
  • 相关阅读:
    c# 菱形,三角形
    c#判断一个时间的时间段
    c# 1,判断是否为正整数 2 判断体重
    数据库 基础
    c#100 计算行李重量
    c#基础 1,100以内的与7相关的数字;2,计算器,
    String 类;Math 类
    常用的C#类
    冒泡排序与快速排序
    数据库基本知识
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9449376.html
Copyright © 2020-2023  润新知