zoukankan      html  css  js  c++  java
  • 皮卡丘的梦想2(线段树+二进制状态压缩)

    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 的百分号 "%"(不包括引号)。

    Sample 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
    

    Sample Output

    Case 1:
    %
    50
    50
    20 50
    %
    %
    
     解题思路:刚好暑假学完了线段树,我一开始就把这道题当做线段树的裸题,用线段树来维护数据,不就是单点更新,区段查询嘛。但是很不幸时间超限,后来我考虑了原来每次查询进化石的时候还是要全部遍历一遍,所以是要超时的。这时我们就需要更高效的方法来存储和查询所有进化石的状态,这种方法就是利用二进制进行状态压缩。
    首先我们要明确一点加入的是第几种进化石,而不是几块进化石,也就是看的是存不存在,而不是存在的数量!!

    我们用二进制数的每一位来表示每种进化石的有无,也即该小镇进化石的存在状态。例如,对于有 1、4 号进化石的小镇,我们可以用二进制数 1001 来表示。它的含义是:从右向左依次表示第 1 个到第 n 个进化石的有无,1 表示有,0 表示无。而且很容易想到,由于每一位只有 0 或 1 两种可能,且每一位都对应固定的编号,所以对于任意一个二进制数,都能保证唯一对应一种存在状态。

    解决了如何表示存在状态的问题,下一步就是如何存储了。例如,当前我们的进化石存在状态为:1、4,对应二进制 1001,如果我们加入一个 3 号进化石,则应变为 1101,也就是让倒数第三位变成 1。这里需要用到位运算:对于 1001,我们让它与 0100(只含有 3 号石的状态)进行或运算,即两数对应的位有一个或两个为 1 时结果为1,否则为 0,运算结果为 1101。这样我们使用或运算就可以实现两个状态的合并。至于如何表示单个进化石的状态,很简单,使用左移运算就可以了,例如:表示 3 号石存在,只需将 1(0001)左移 3-1=2 位即得到 0100。

    这样,我们只需要把二进制和线段树结合一下就可以愉快地告别 TLE 了。在存储时,每一个结点都表示它的左右子结点的合并状态,即对左右子结点进行或运算后的结果,而叶结点直接存储状态。在查询时,只需要遍历结果对应二进制的每一位来输出即可。

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #define ll long long int
      5 const int MAXN=1e5+10;
      6 using namespace std;
      7 int n,q;
      8 ll sum[MAXN<<2];
      9 void push_up(int i)///向上回溯,状态合并
     10 {
     11     sum[i]=sum[i<<1]|sum[i<<1|1];
     12 }
     13 void build(int l,int r,int rt)///建树
     14 {
     15     if(l==r)
     16     {
     17         sum[rt]=0;
     18         return ;
     19     }
     20     int mid=(r+l)>>1;
     21     build(l,mid,rt<<1);
     22     build(mid+1,r,rt<<1|1);
     23     push_up(rt);
     24 }
     25 void add(int l,int r,int pos,int v,int rt)
     26 {
     27     if(l==r)
     28     {
     29         sum[rt]|=1ll<<(v-1);///1ll为长整型的1
     30         return ;
     31     }
     32     int m=(r+l)>>1;
     33     if(m>=pos)
     34     {
     35         add(l,m,pos,v,rt<<1);
     36     }
     37     else
     38     {
     39         add(m+1,r,pos,v,rt<<1|1);
     40     }
     41     push_up(rt);
     42 }
     43 void del(int l,int r,int pos,int val,int rt)
     44 {
     45     if(l==r)
     46     {
     47         sum[rt]&=~(1ll<<(val-1));
     48         return ;
     49     }
     50     int mid=(r+l)>>1;
     51     if(pos<=mid)
     52     {
     53         del(l,mid,pos,val,rt<<1);
     54     }
     55     else
     56     {
     57         del(mid+1,r,pos,val,rt<<1|1);
     58     }
     59     push_up(rt);
     60 }
     61 ll query(int l,int r,int L,int R,int rt)///区间查询
     62 {
     63     if(L<=l&&R>=r)
     64     {
     65         return sum[rt];
     66     }
     67     ll ans=0;
     68     int mid=(r+l)>>1;
     69     if(L<=mid)
     70     {
     71         ans|=query(l,mid,L,R,rt<<1);
     72     }
     73     if(R>mid)
     74     {
     75         ans|=query(mid+1,r,L,R,rt<<1|1);
     76     }
     77     return ans;
     78 }
     79 
     80 int main()
     81 {
     82     int i,t;
     83     int op,x,y;
     84     scanf("%d",&t);
     85     for(i=1; i<=t; i++)
     86     {
     87         printf("Case %d:
    ",i);
     88         scanf("%d%d",&n,&q);
     89         build(1,n,1);
     90         while(q--)
     91         {
     92             scanf("%d",&op);
     93             scanf("%d%d",&x,&y);
     94             if(op==1)
     95             {
     96                 add(1,n,x,y,1);
     97             }
     98             if(op==2)
     99             {
    100                 del(1,n,x,y,1);
    101             }
    102             if(op==3)
    103             {
    104                 ll ans=query(1,n,x,y,1);
    105                 ll cnt=0;
    106                 int flag=1;
    107                 int ot=1;
    108                 while(ans)
    109                 {
    110                     if(ans&1)
    111                     {
    112                         if(flag)
    113                         {
    114                             flag=0;
    115                         }
    116                         else
    117                         {
    118                             printf(" ");
    119                         }
    120                         printf("%d",ot);
    121                     }
    122                     ot++;
    123                     ans>>=1;
    124                 }
    125                 if(flag)
    126                 {
    127                     printf("%%
    ");
    128                 }
    129                 else
    130                 {
    131                     printf("
    ");
    132                 }
    133             }
    134         }
    135     }
    136     return 0;
    137 }
  • 相关阅读:
    Thread.join()的使用
    Java 编程思想
    LoadRunner 常见错误
    Selenium+IDEA+Maven+TestNG环境搭建
    计算机基本知识了解(二)
    Java中int和Integer的区别
    计算机基本知识了解(一)
    Jmeter安装及环境配置
    app稳定性测试-monkey测试
    .net framework
  • 原文地址:https://www.cnblogs.com/wkfvawl/p/9557057.html
Copyright © 2011-2022 走看看