牛客 某练习赛 Data Structure

Data Structure

题目描述

将一个非负整数序列划分为 \(K\) 段,分别计算出各段中的整数按位或的结果,然后再把这些结果按位与起来得到一个最终结果,把这个最终结果定义为这个序列的一个 \(K−or−and\) 值。

比如序列为 \([1,5,9,2],K=2\),如果划分为 \([1,5],[9,2]\),那么 \(K−or−and\) 值为 \((1or5)and(9or2)=1\)。当然划分可能不止一种,所以 \(K−or−and\) 值也可能不止一个。

给定一个长度为 \(N\) 的非负整数序列 \(A_1A_2A_3\dots A_N\),一个整数 \(K\) 和以下三种操作:

  1. 给定一个整数 \(x\) ,把序列中的所有数字按位或上 \(x\)。即\(\forall i\in [1,N],A_i=A_i \ or \ x\)

  2. 给定一个整数 \(x\),把序列中的所有数字按位与上 \(x\)。即\(\forall i\in [1,N],A_i=A_i \ and \ x\)

  3. 查询当前序列最大的 \(K−or−and\) 值。

lililalala太菜了,他希望你来帮他解决这个问题。

输入描述:

第一行两个整数 \(N,K(1\le K\le N\times 2\times 10^5)\) -序列长度和划分的段数。
第二行 \(N\)个整数 \(A_1A_2A_3\dots A_N( 0\le A_1,A_2,A_3\dots A_N<2^{31})\)
第三行一个整数 \(Q(1\le Q\le 2\times 10^5)\)--操作的数量。
然后 \(Q\) 行其中第 \(i\)行为以下三种格式之一:
\(1x_i\)--把序列中的所有数字按位或上 \(x_i(0\le x_i<2^{31})\)
\(2x_i\)--把序列中的所有数字按位与上 \(x_i(0\le x_i<2^{31})\)
\(3\)--查询当前序列最大的 \(K−or−and\) 值。

输出描述:

对于每次查询(操作 \(3\) )输出一行一个整数作为查询结果。


1394419-20190504164851098-1257545948.png

昨天晚上云做题,看了看C和D

然后成功嘴巴出做法,但是一直wa,今天顺手改一改,C比较简单,不管了

先不管修改,我们直接看看怎么求原序列的答案

显然可以按位贪心

如果我们可以确定高\(i\)位的答案为\(ans\),那么考虑\(i+1\)位是否可以取\(1\),可以直接贪心考虑

从左到右扫\(A\)数组,如果当前前缀异或值\(x\)可以拼出高\(i+1\)位的所有\(1\),就搞一个分割。

这样显然可以最大化分割。

然后复杂度\(n \log A_i\)

考虑操作每次会把所有的某一位取\(0\)或者\(1\),如果某一维被取过,这一位就不用管了。

发现这样最多有\(\log A_i\)个位被第一次全部取\(0\)或者\(1\)

那么直接每次暴力就好了

复杂度\(O(n\log^2 A)\)


Code:

#include <cstdio>
#include <cctype>
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
    x=0;char c=gc();
    while(!isdigit(c)) c=gc();
    while(isdigit(c)) x=x*10+c-'0',c=gc();
}
const int N=2e5+10;
int yuy[32],a[N];
int n,k,Q,ans;
void getans()
{
    ans=0;
    for(int j=30;~j;j--)
    {
        if(yuy[j]!=-1) continue;
        int cnt=0,x=0;
        ans|=1<<j;
        for(int i=1;i<=n;i++)
        {
            x|=a[i];
            if((ans&x)==ans) ++cnt,x=0;
        }
        if(cnt<k) ans^=1<<j;
    }
    for(int j=0;j<=30;j++)
    {
        if(yuy[j]==1)
            ans|=1<<j;
        else if(yuy[j]==0)
            ans&=~(1<<j);
    }
}
int main()
{
    read(n),read(k);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int j=0;j<=30;j++) yuy[j]=-1;
    getans();
    read(Q);
    for(int op,x,i=1;i<=Q;i++)
    {
        read(op);
        int flag=0;
        if(op==1)
        {
            read(x);
            for(int j=30;~j;j--)
                if(x>>j&1)
                {
                    if(yuy[j]==-1) flag=1;
                    yuy[j]=1;
                }
            if(flag) getans();
        }
        else if(op==2)
        {
            read(x);
            for(int j=30;~j;j--)
                if(!(x>>j&1))
                {
                    if(yuy[j]==-1) flag=1;
                    yuy[j]=0;
                }
            if(flag) getans();
        }
        else
            printf("%d\n",ans);
        for(int j=0;j<=30;j++)
        {
            if(yuy[j]==1)
                ans|=1<<j;
            else if(yuy[j]==0)
                ans&=~(1<<j);
        }
    }
    return 0;
}

2019.5.4

转载于:https://www.cnblogs.com/butterflydew/p/10808931.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值