HDU 6085 Rikka with Candies

之前不知道bitset这种结构,估计知道了,也想不起来用,看博客说STL里面没有区间操作,用的话会超时,要自己手写,这里我找了网上大牛手写的bitset好好解析了一番,解题思路的话就是由于只需要求出种数的奇偶性,容易发现奇偶性的变化和2进制中亦或的结果相同。于是想到利用位操作来进行优化。对于Bi ,可以不断的枚举区间 [kBi,(k+1)Bi1] ,而这一段区间中值 kBi+d 对Bi 取模的结果为 d ,即该区间对Bi 取模结果为区间 [0,Bi1] 。于是根据 Ai 的值将取放置到区间中的对应位置,再不断枚举区间 [kBi,(k+1)Bi1] 和区间 [0,B1]做亦或操作。询问时只要查询对应位的01值即可。实现时发现bitset没有区间操作的功能,所有利用unsigned long long 手动实现了bitset。最后复杂度为 O(n264) 。

以上摘自大牛题解:

自己注释过的代码,帮助理解:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ULL unsigned long long
const int maxn=5e4+50;
struct Bitset
{
    ULL bit[1600];
    void reset()
    {
        memset(bit,0,sizeof bit);
    }
    void flip(int pos)//把第pos位置一
    {
        bit[pos>>6]^=1llu<<(pos&63);
    }
    bool judge(int pos)//判断第pos位是否是1
    {
        return bit[pos>>6]&(1llu<<(pos&63));
    }
}A,ans;

int b[maxn];
ULL pre[66];
int main()
{
    freopen("input.txt","r",stdin);
    int T,n,m,q,a,mx;
    scanf("%d",&T);
    pre[0]=1;
    for(int i=1;i<64;i++)
        pre[i]=pre[i-1]|(1ll<<i);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&q);
        A.reset();
        ans.reset();
        mx=0;//最大的ax,是数据处理的范围
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a);
            A.flip(a);
            mx=max(a,mx);
        }
        for(int i=0;i<m;i++)
        {
           scanf("%d",&b[i]);
           for(int j=0;j*b[i]<=mx;j++)
           {
               int len=j*b[i];
               int idx1=len>>6,p1=len&63;
               len+=b[i]-1;
               int idx2=len>>6,p2=len&63;
               if(idx1==idx2)//在同一组64位bit里面
               {
                   ULL tmp=A.bit[idx1]>>p1;
                   tmp&=pre[p2-p1];//高位清零
                   ans.bit[0]^=tmp;//得出其中一组b区间的贡献
               }
               else//b的异或区间跨越多个64位组
               {
                   int curid=0,curpos=0;//ans的异或起始区间
                   ULL tmp=A.bit[idx1]>>p1;
                   tmp&=pre[63-p1];//从P1到本组最高位都要操作
                   ans.bit[0]^=tmp;
                   curpos=63-p1;//已经操作至第curpos位
                   for(int k=idx1+1;k<idx2;k++)
                   {
                       if(curpos==63)//说明p1是从第0位操作的,那么接下来的所有整组可以直接操作
                       {
                          ans.bit[++curid]^=A.bit[k];
                       }
                       else//p1不是从第0位操作的,那么就是半组操作
                        {
                            tmp=A.bit[k]<<(curpos+1);//补答案bit中的高位
                            ans.bit[curid++]^=tmp;
                            tmp=A.bit[k]>>p1;//继续操作补完高位后剩下的低位
                            tmp&=pre[curpos];
                            ans.bit[curid]^=tmp;//答案bit中新的一组又操作了pos位
                        }
                   }
                   if(curpos==63)//相当于p1等于0了
                   {
                       curid++;
                       tmp=A.bit[idx2]&pre[p2];
                       ans.bit[curid]^=tmp;
                   }
                   else
                   {
                       if(p2<p1)//直接补上最后p2个,因为
                       {
                           tmp=A.bit[idx2]&pre[p2];
                           tmp<<=curpos+1;//左移之后,把最后的p2位来补高位,同上
                           ans.bit[curid]^=tmp;
                       }
                       else//p2比较大的时候,要分成两次,第一次补高位,加下来操作剩余位
                       {
                           tmp=A.bit[idx2]<<(curpos+1);
                           ans.bit[curid++]^=tmp;
                           tmp=A.bit[idx2]>>p1;
                           tmp&=pre[p2-p1];
                           ans.bit[curid]^=tmp;
                       }
                   }
               }
           }
        }
        while(q--)
        {
            int k;
            scanf("%d",&k);
            if(ans.judge(k))
                printf("1\n");
            else
                printf("0\n");
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/MeowMeowMeow/p/7389068.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值