【loli的胡策】测试4.27(位运算乱搞)

本文介绍了一种解决特定二进制序列更新与查询问题的方法。通过使用线段树和bitset来高效处理序列更新,并确保序列满足特定的二进制约束条件。文章提供了两种实现方案,一种适用于80%的数据点,另一种则可以达到100%的通过率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T1:

这里写图片描述
这里写图片描述

题解:

分类讨论一下,如果前面的数>后面的数,那么对应2进制位前面的为0,后面的为1的最高位不能异或;如果前面的数< 后面的数,那么前面为1,后面为0的最高位必须异或;等于的话随便异或
那么我们有两个条件,一个是允许(ok),另一个是必须(must),如果must的不ok就是-1,如果不是-1就把所有must的二进制加起来

考场上写了O(qlog2n)O(qlog2n)的线段树80pts,每个叶子节点是相邻两个节点的must和ok,如果用bitset维护的话,update的时候就可以直接ok&,must|。那么每次修改相关的只有这个点和左边的点&&这个点和右边的点,复杂度O(log2n)O(log2n)

考虑优化。可以发现每次修改x,都只跟x-1和x+1有关,那么每次修改都记录不被允许&&必须的个数,不被允许的那一位不为0意为不允许,必须的那一位不为0意为必须,单次修改O(logn),可以通过100pts

代码:

80pts(数据结构学傻的人

#include <cstring>
#include <cstdio>
#include <bitset>
#define LL long long
using namespace std;
const int sz=35;
const int N=2000005;
bitset <sz> ok[N*4],must[N*4];
int num[N+5][sz+5];LL a[N+5];
void updata(int now)
{
    must[now]=must[now<<1]|must[now<<1|1];
    ok[now]=ok[now<<1]&ok[now<<1|1];
}
void build(int now,int l,int r)
{
    if (l==r)
    {
        for (int i=0;i<=sz;i++) ok[now][i]=1,must[now][i]=0;
        if (a[l]<=a[l+1])
        {
        for (int j=sz;j>=0;j--)
          if (num[l][j]==0 && num[l+1][j]==1) {ok[now][j]=0; break;}
        }else
        {
        for (int j=sz;j>=0;j--)
          if (num[l][j]==1 && num[l+1][j]==0) {must[now][j]=1; break;}
        }
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    updata(now);
}
void change(int now,int l,int r,int x)
{
    if (l==r)
    {
        for (int i=0;i<=sz;i++) ok[now][i]=1,must[now][i]=0;
        if (a[l]<=a[l+1])
        {
        for (int j=sz;j>=0;j--)
          if (num[l][j]==0 && num[l+1][j]==1) {ok[now][j]=0; break;}
        }else
        {
        for (int j=sz;j>=0;j--)
          if (num[l][j]==1 && num[l+1][j]==0) {must[now][j]=1; break;}
        }
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) change(now<<1,l,mid,x);
    else change(now<<1|1,mid+1,r,x);
    updata(now);
}
LL work()
{
    LL ans=0;
    for (int i=sz;i>=0;i--)
      if (must[1][i] && !ok[1][i]) return -1;
    for (int i=sz;i>=0;i--)
      if (must[1][i]) ans|=(1<<i);
    return ans;
}
int main()
{
    freopen("repair.in","r",stdin);
    freopen("repair.out","w",stdout);
    int n;scanf("%d",&n);
    for (int i=1;i<=n;i++) 
    {
        scanf("%lld",&a[i]);
        for (int j=sz;j>=0;j--)
          if (a[i]>>j&1) num[i][j]=1;else num[i][j]=0;
    }
    build(1,1,n-1);
    if (n==1) printf("0");
    else printf("%lld\n",work());
    int q;scanf("%d",&q);
    while (q--)
    {
        int x;LL v;
        scanf("%d%lld",&x,&v);
        a[x]=v;
        for (int j=sz;j>=0;j--)
          if (a[x]>>j&1) num[x][j]=1;else num[x][j]=0;
        change(1,1,n-1,x); change(1,1,n-1,x-1);
        if (n==1) printf("0");
        else printf("%lld\n",work());
    }
}

100pts

#include <cstdio>
#define LL long long
using namespace std;
const int sz=35;
const int N=2000005;
int num[N+5][sz+5],ok[N],must[N];LL a[N];
int find(LL x,LL y)
{
    LL xin=x^y;
    for (int i=sz;i>=0;i--)
      if (xin>>i&1) return i;
}
LL work()
{
    LL ans=0;
    for (int i=sz;i>=0;i--)
      if (must[i] && ok[i]) return -1;
    for (int i=sz;i>=0;i--)
      if (must[i]) ans|=(1<<i);
    return ans;
}
int main()
{
    freopen("repair.in","r",stdin);
    freopen("repair.out","w",stdout);
    int n;scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for (int i=2;i<=n;i++)
      if (a[i-1]<a[i]) ok[find(a[i-1],a[i])]++;
      else if (a[i-1]>a[i]) must[find(a[i-1],a[i])]++;
    printf("%lld\n",work());
    int q;scanf("%d",&q);
    while (q--)
    {
        int i;LL v;
        scanf("%d%lld",&i,&v);
        if (i!=1)
        {
            if (a[i-1]<a[i]) ok[find(a[i-1],a[i])]--;
            else if (a[i-1]>a[i]) must[find(a[i-1],a[i])]--;
        }
        if (i!=n)
        {
            if (a[i]<a[i+1]) ok[find(a[i],a[i+1])]--;
            else if (a[i]>a[i+1]) must[find(a[i],a[i+1])]--;
        }
        a[i]=v;
        if (i!=1)
        {
            if (a[i-1]<a[i]) ok[find(a[i-1],a[i])]++;
            else if (a[i-1]>a[i]) must[find(a[i-1],a[i])]++;
        }
        if (i!=n)
        {
            if (a[i]<a[i+1]) ok[find(a[i],a[i+1])]++;
            else if (a[i]>a[i+1]) must[find(a[i],a[i+1])]++;
        }
        printf("%lld\n",work());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值