皮卡丘的梦想2(线段树+位运算)

皮卡丘的梦想2
Time Limit: 1000MS Memory Limit: 65536KB
Submit Statistic
Problem Description

一天,一只住在 501 实验室的皮卡丘决定发奋学习,成为像 LeiQ 一样的巨巨,于是他向镇上的贤者金桔请教如何才能进化成一只雷丘。
金桔告诉他需要进化石才能进化,并给了他一个地图,地图上有 n 个小镇,他需要从这些小镇中收集进化石。
接下来他会进行 q 次操作,可能是打听进化石的信息,也可能是向你询问第 l 个小镇到第 r 个小镇之间的进化石种类。
如果是打听信息,则皮卡丘会得到一个小镇的进化石变化信息,可能是引入了新的进化石,也可能是失去了全部的某种进化石。
如果是向你询问,你需要回答他第 l 个小镇到第 r 个小镇之间的进化石种类。
Input

首先输入一个整数 T (1 <= T <= 10),代表有 T 组数据。
每组数据的第一行输入一个整数 n (1 <= n <= 100000) 和一个整数 q (1 <= q <= 100000),分别代表有 n 个小镇,表皮卡丘有 q 次操作。
接下来输入 q 行,对于每次操作,先输入操作类型,然后根据操作类型读入:
1: 紧接着输入 2 个整数 a (1 <= a <= n), b (1 <= b <= 60),表示第 a 个小镇引入了第 b 种进化石
2: 紧接着输入 2 个整数 a (1 <= a <= n), b (1 <= b <= 60),表示第 a 个小镇失去了全部第 b 种进化石
3: 紧接着输入 2 个整数 l, r (1 <= l <= r <= n),表示他想询问从第 l 个到第 r 个小镇上可收集的进化石有哪几种
Output

对于每组输入,首先输出一行 “Case T:”,表示当前是第几组数据。
对于每组数据中的每次 3 操作,在一行中按编号升序输出所有可收集的进化石。如果没有进化石可收集,则输出一个 MeiK 的百分号 “%”(不包括引号)。
Example Input

1
10 10
3 1 10
1 1 50
3 1 5
1 2 20
3 1 1
3 1 2
2 1 50
2 2 20
3 1 2
3 1 10
Example Output

Case 1:
%
50
50
20 50
%
%
题解:很明显是用线段树来维护操作。可是用什么来存储状态呢?想了想,用二进制01来表示石头的有无。然后用位运算来支持操作。
代码:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<string.h>
#define ll long long
using namespace std;
const int N=1e5+10;
ll  sum[N<<2];
int t,n,q;
int pos,val,op;
void pushup(int rt)
{
    sum[rt]=sum[rt<<1]|sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=0;
        return ;
    }
    int mid=(r+l)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void add(int l,int r,int pos,int val,int rt)
{
    if(l==r)
    {
        sum[rt]|=1ll<<(val-1);
        return ;
    }
    int mid=(r+l)>>1;
    if(pos<=mid)
    add(l,mid,pos,val,rt<<1);
    else
    add(mid+1,r,pos,val,rt<<1|1);
    pushup(rt);
}
void del(int l,int r,int pos,int val,int rt)
{
     if(l==r)
    {
        sum[rt]&=~(1ll<<(val-1));
        return ;
    }
    int mid=(r+l)>>1;
    if(pos<=mid)
    del(l,mid,pos,val,rt<<1);
    else
    del(mid+1,r,pos,val,rt<<1|1);
    pushup(rt);

}
ll query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&R>=r)
    {
        return sum[rt];
    }
    ll ans=0;
    int mid=(r+l)>>1;
    if(L<=mid)
    ans|=query(l,mid,L,R,rt<<1);
    if(R>mid)
    ans|=query(mid+1,r,L,R,rt<<1|1);
    return ans;
}
int main()
{
    scanf("%d",&t);
   for(int k=1;k<=t;k++)
   {
       printf("Case %d:\n",k);
        scanf("%d%d",&n,&q);
        build(1,n,1);
        while(q--)
        {
            scanf("%d",&op);
            scanf("%d%d",&pos,&val);
            if(op==1)
            {
                add(1,n,pos,val,1);
            }
            if(op==2)
            {
                del(1,n,pos,val,1);

            }
            if(op==3)
            {
                ll ans=query(1,n,pos,val,1);
                ll cnt=0;
                bool flag=true;
                int ot=1;
                while(ans)
                {
                    if(ans&1)
                    {
                        if(flag) flag=false;
                       else printf(" ");
                        printf("%d",ot);
                    }
                    ot++;
                    ans>>=1;
                }
                if(flag)
                {
                    printf("%%\n");
                }
                else
                printf("\n");

            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值