Codeforces Round 930 (Div. 2) ---- F. Bitwise Paradox ---- 题解

F. Bitwise Paradox:

题目描述:

思路解析:

因为是或运算,可以想到如果区间越大,值肯定越大,但是选的区间越大,max值可能会变得更大,其实不用太大就可以得到答案。

那么对于查询区间 [l,r],将其划分为两个区间, 答案区间可能在左区间,也有可能在右区间,也可能即包含左区间也包含右区间。那么我们需要查询区间的a数组最大值,这里可以使用跳表预处理,然后o1的查询区间最大值,如果是实时查询的数据结构,会让时间复杂度变为Q*logN*logN*logV,因为需要查询的区间过多。(每判断一个区间就会查询一次logV*logN次)。然后查询区间是否合法的话,看起来十分像线段树的操作过程。

那我们对于每个区间只需要维护或值刚好大于等于V的最佳答案。然后在往上合并的时候,我们再维护跨过两个区间的答案。那么假设 A 区间 和 B 区间, 合并时我们对于V从二进制的每一位(高位到低位)进行枚举,那么如果V在这位为1,A和B区间上的或值在这位为0,那么后面的位枚举都是没有意义的。 如果 A和B 在这一位都为1 (如果只有一个为1,那就选择对应区间即可),那么假设我们知道A使得这位为1的最右数字是那个(显然这可以利用线段树维护出来),知道B使得这位为0的最左数字是那个。(为什么选择最左和最右,因为从贪心角度来看,如果小区间的max值一定小于等于大区间的max值,所以优先选择靠近中间点的这两个)那这两个选那个?如果 [l,m] <= [m+1,r] 的最大值,那么选择A的最右,否则选择B的最左。

如果V在这位为0,如果A和B区间在这位为1,那我们选上这位,后面不用判断都肯定大于V,那我们看选择了这一位能不能使得区间最大值变小。

那么区间合并完成,修改的时候,只需要维护单点在哪些位为1,和判断单点值是否大于V即可。

那么这道题的复杂度为 O(Q*logN*logV + N*logN) ,总所周知Java很容易被卡掉,尤其是大型数据结构,那么这道题java被卡了,但是网上的cpp代码我个人认为码风不太好,所以给出我的Java代码帮助大家理解,给出正确的cpp代码给大家参考。

JAVA代码实现:



import java.io.*;
import java.math.BigInteger;
import java.util.*;


public class Main {
    static int inf = (int) 1e9;

    public static void main(String[] args) throws IOException {
        int t = f.nextInt();
        while (t > 0) {
            solve();
            t--;
        }

        w.flush();
        w.close();
        br.close();
    }

    static int [] a;
    static int[] b;
    static int v;
    static ST st;

    public static void solve() {
        int n = f.nextInt(); v = f.nextInt();
        a = new int[n+1];
        b = new int[n+1];
        for (int i = 1; i <= n; i++) {
            a[i] = f.nextInt();
        }
        for (int i = 1; i <= n; i++) {
            b[i] = f.nextInt();
        }
        st = new ST();
        st.init(n);
        SegmentTree tree = new SegmentTree();
        tree.build(1, 1, n);
        int q = f.nextInt();
        for (int i = 0; i < q; i++) {
            int op = f.nextInt();
            int l = f.nextInt(); int r = f.nextInt();
            if (op == 1){
                tree.change(1, l, r);
            }else {
                Node A = tree.qry(1, l, r);
                int ans = A.res == inf ? -1 : A.res;
                w.print(ans + " ");
            }
        }
        w.println();

    }

    static int N = (int) 2e5 + 5;

    public static class ST{
        int f[][] = new int[N][20];
        int[] _log = new int[N];
        void init(int n)
        {
            int i,j; for (_log[0]=-1,i=1;i<=n;++i) _log[i]=_log[i>>1]+1;
            for (i=1;i<=n;++i) f[i][0]= a[i];
            for (j=1;(1<<j)<=n;++j) for (i=1;i+(1<<j)-1<=n;++i)
                f[i][j]= Math.max(f[i][j-1],f[i+(1<<j-1)][j-1]);
        }
        int querymax(int l,int r)
        {
            if (l>r) return inf; int k=_log[r-l+1];
            return Math.max(f[l][k],f[r-(1<<k)+1][k]); // 计算l-r的a数组最大值
        }
    }

    public static class SegmentTree{
        Node[] t = new Node[N  << 2];

        public SegmentTree() {
            for (int i = 0; i < N << 2; i++) {
                t[i] = new Node();
            }
        }

        void build(int root, int l, int r){
            t[root].l = l; t[root].r = r;
            if (l == r){
                for (int i = 29; i >= 0; i--) {
                    if (((b[l] >> i)& 1) == 1) t[root].fir[i] = t[root].lst[i] = l;
                }
                if (b[l] >= v) t[root].res = a[l];
                return;
            }
            int m = (l + r) >> 1;
            int ch = root << 1;
            build(ch, l, m); build(ch|1, m+1, r);
            merge(t[root], t[ch], t[ch|1], m);
        }
        void merge(Node C, Node A, Node B, int m){

            C.res = Math.min(A.res, B.res); int L = m+1; int R = m; int flag = 1;
            for (int i = 0; i < 30; i++) {
                C.fir[i] = A.fir[i] > 0? A.fir[i] : B.fir[i];
                C.lst[i] = B.lst[i] > 0? B.lst[i] : A.lst[i];
            }
            for (int i = 29; i >= 0; i--) {
                if (((v >> i) & 1) == 1){ // v 包含这个数
                    int l = A.lst[i] > 0 ? A.lst[i] : m+1; int r = B.fir[i] > 0? B.fir[i] : m;
                    int mxl = st.querymax(l, m); int mxr = st.querymax(m+1, r);
                    if (mxl == inf && mxr == inf) {flag = 0; break;}
                    if (mxl <= mxr) L = Math.min(L, l); else R = Math.max(R, r);
                }else {
                    int l = A.lst[i] > 0 ? A.lst[i] : m+1; int r = B.fir[i] > 0? B.fir[i] : m; int tl = L; int tr = R;
                    int mxl = st.querymax(l, m); int mxr = st.querymax(m+1, r);
                    if (mxl == inf && mxr == inf) continue;
                    if (mxl <= mxr) tl = Math.min(L, l); else tr = Math.max(R, r);
                    C.res = Math.min(C.res, st.querymax(tl, tr));
                }
            }
            if (flag == 1) C.res = Math.min(C.res, st.querymax(L, R));
        }
        void change(int root, int l, int val){
            if (t[root].l == t[root].r){
                t[root] = new Node();
                t[root].l = t[root].r = l;
                b[l] = val;
                for (int i = 29; i >= 0; i--) {
                    if (((b[l] >> i)& 1) == 1) t[root].fir[i] = t[root].lst[i] = l;
                }
                if (b[l] >= v) t[root].res = a[l];
                return;
            }
            int m = (t[root].l + t[root].r) >> 1;
            int ch = root << 1;
            if (l <= m) change(ch, l, val);
            else  change(ch|1, l, val);
            merge(t[root], t[ch], t[ch|1], m);
        }
        Node qry(int root, int l, int r){
            if (t[root].l >= l && t[root].r <= r) return t[root];
            int m = (t[root].l + t[root].r) >> 1; int ch = root << 1;
            if (r <= m) return qry(ch, l, r);
            else if (l > m) return qry(ch|1, l,r);
            else {
                Node C = new Node();
                Node A = qry(ch, l, m); Node B = qry(ch|1, m+1, r);
                merge(C, A, B, m);
                return C;
            }
        }

    }
    public static class Node{
        int l, r, res;
        int[] fir; int[] lst;

        public Node() {
            this.res = inf;
            fir = new int[30]; lst = new int[30];
        }
    }

    static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
    static Input f = new Input(System.in);
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public String nextLine() {
            String str = null;
            try {
                str = reader.readLine();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return str;
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public Double nextDouble() {
            return Double.parseDouble(next());
        }

        public BigInteger nextBigInteger() {
            return new BigInteger(next());
        }
    }
}

CPP代码实现:

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005,INF=2e9;
int t,n,v,a[N],b[N],q,opt,x,y;
namespace RMQ
{
    int f[N][20],_log[N];
    inline void init(CI n)
    {
        RI i,j; for (_log[0]=-1,i=1;i<=n;++i) _log[i]=_log[i>>1]+1;
        for (i=1;i<=n;++i) f[i][0]=a[i];
        for (j=1;(1<<j)<=n;++j) for (i=1;i+(1<<j)-1<=n;++i)
                f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
    }
    inline int querymax(CI l,CI r)
    {
        if (l>r) return INF; int k=_log[r-l+1];
        return max(f[l][k],f[r-(1<<k)+1][k]);
    }
};
using namespace RMQ;
class Segment_Tree
{
private:
    struct segment
    {
        int res,fir[30],lst[30];
        inline segment(void)
        {
            res=INF; memset(fir,0,sizeof(fir)); memset(lst,0,sizeof(lst));
        }
    }O[N<<2];
    inline segment merge(const segment& A,const segment& B,CI mid)
    {
        segment C=segment(); C.res=min(A.res,B.res); int L=mid+1,R=mid; bool flag=1;
        for (RI i=0;i<30;++i) C.fir[i]=A.fir[i]?A.fir[i]:B.fir[i],C.lst[i]=B.lst[i]?B.lst[i]:A.lst[i];
        for (RI i=29;i>=0;--i) if ((v>>i)&1)
            {
                int l=A.lst[i]?A.lst[i]:mid+1,r=B.fir[i]?B.fir[i]:mid;
                int mxl=querymax(l,mid),mxr=querymax(mid+1,r);
                if (mxl==INF&&mxr==INF) { flag=0; break; }
                if (mxl<=mxr) L=min(L,l); else R=max(R,r);
            } else
            {
                int l=A.lst[i]?A.lst[i]:mid+1,r=B.fir[i]?B.fir[i]:mid,tl=L,tr=R;
                int mxl=querymax(l,mid),mxr=querymax(mid+1,r);
                if (mxl==INF&&mxr==INF) continue;
                if (mxl<=mxr) tl=min(L,l); else tr=max(R,r);
                C.res=min(C.res,querymax(tl,tr));
            }
        if (flag) C.res=min(C.res,querymax(L,R)); return C;
    }
public:
#define TN CI now=1,CI l=1,CI r=n
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
    inline void build(TN)
    {
        O[now]=segment(); if (l==r)
        {
            for (RI i=0;i<30;++i)
                if ((b[l]>>i)&1) O[now].fir[i]=O[now].lst[i]=l;
            if (b[l]>=v) O[now].res=a[l]; return;
        }
        int mid=l+r>>1; build(LS); build(RS);
        O[now]=merge(O[now<<1],O[now<<1|1],mid);
    }
    inline void modify(CI pos,CI mv,TN)
    {
        if (l==r)
        {
            O[now]=segment(); b[l]=mv;
            for (RI i=0;i<30;++i)
                if ((b[l]>>i)&1) O[now].fir[i]=O[now].lst[i]=l;
            if (b[l]>=v) O[now].res=a[l]; return;
        }
        int mid=l+r>>1; if (pos<=mid) modify(pos,mv,LS); else modify(pos,mv,RS);
        O[now]=merge(O[now<<1],O[now<<1|1],mid);
    }
    inline segment query(CI beg,CI end,TN)
    {
        if (beg<=l&&r<=end) return O[now]; int mid=l+r>>1;
        if (end<=mid) return query(beg,end,LS); else
        if (beg>mid) return query(beg,end,RS); else
            return merge(query(beg,end,LS),query(beg,end,RS),mid);
    }
    inline int Query(CI beg,CI end)
    {
        return query(beg,end).res;
    }
#undef TN
#undef LS
#undef RS
}SEG;
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    for (scanf("%d",&t);t;--t)
    {
        RI i; for (scanf("%d%d",&n,&v),i=1;i<=n;++i) scanf("%d",&a[i]);
        for (init(n),i=1;i<=n;++i) scanf("%d",&b[i]);
        for (SEG.build(),scanf("%d",&q),i=1;i<=q;++i)
        {
            scanf("%d%d%d",&opt,&x,&y);
            if (opt==1) SEG.modify(x,y); else
            {
                int tmp=SEG.Query(x,y);
                printf("%d%c",tmp!=INF?tmp:-1," \n"[i==q]);
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值