• [XJOI]noip44 T3还有这种操作


    还有这种操作

    ttt 最近在学习二进制, 他想知道小于等于N的数中有多少个数的二进制表示中有偶数个1。

    但是他想了想觉得不够dark,于是他增加了若干次操作,每次操作会将一个区间内的0变1 , 1变0,

    现在在每次操作之后,他都想知道原来的那个问题的答案。

    输入格式:

    第一行:一个01序列表示N的二进制表示 
    第二行:操作次数 m 
    接下来的m行每行两个数,表示要操作的区间。

    输出格式:

    输出m+1行,第一行表示初始的答案,第二行到第m+1行输出每次操作后的答案 
    答案对1e9 + 7 取模

    样例输入:

    0
    1
    1 1

    样例输出:

    1
    1

    数据范围:

    设 n为 N 的 位数

    • 10%, n <= 20, m <= 100
    • 30%, n <= 60, m <= 10000
    • 60%, n <= 1000, m <= 10000
    • 100%, n <= 1000000, m <= 100000

    解题思路:

    在看到n<=2^1000000时,我的内心是震惊的,想着或者是字典树,线段树

    于是想着线段树方向想,但是怎么统计答案又成为了一个问题...

    加之题目并没有说l<=r,于是这道题就愉快的爆零了

    ***下面是正题

    我们想用一个线段树,维护每一位的翻转次数,以及每一位的答案的值

    那么我们就可以用nlogn的时间建出它,并用mlogn的时间来查询。

    好线段树部分讲完了

    ***下面是关于统计答案的

    我们首先可以发现

    [0,1)内有2^0=1个

    [0,10)内有2^0=1个

    [0,100)内有2^1=2个

    [0,1000)内有2^2=4个(以上数均为二进制表示)

    ...

    那么我们可以总结了,[0,100..00(n个0))内有2^(n-1)个数满足,此时n>=1(为什么下面会讲)

    但是知道这个有什么用么?

    我们想一个 1010=1000+10

    答案就为[0,1000)+[0,10)+check(1010是否满足)

    这个结论的证明留给读者去完成

    ***于是这个程序基本就成型了

    注意:(1)l是可能大于r的

    (2)在对于最后一位处理,需要特殊处理,否则,对于最后一位是1的数,会出现1的差别,

    例如:101 如按原先方法计算,答案为2+1+check(101)=4,而正确答案是3

    因此在遇到原串符合条件,且最后一位是1时,要对答案减一

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=5e6+10,p=1e9+7;
     4 struct xint {int l,r;}a[N];
     5 int val[N],sum[N],pw[N],bo[N],n[N],m,l,r;
     6 char s[N]; bool flag;
     7 void pushup(int rt,int len){
     8     val[rt]=(1ll*val[rt<<1]*pw[len]+val[rt<<1|1])%p;
     9     sum[rt]=sum[rt<<1]^sum[rt<<1|1];
    10 }
    11 void opera(int rt){
    12     bo[rt]^=1; val[rt]=((pw[a[rt].r-a[rt].l+1]-1-val[rt])%p+p)%p;
    13     sum[rt]=((a[rt].r-a[rt].l+1)&1)^sum[rt];
    14 }
    15 void pushdown(int rt){
    16     if (bo[rt]) opera(rt*2),opera(rt*2+1),bo[rt]=0;
    17 }
    18 //--------------------------------
    19 void build(int l,int r,int rt){
    20     if (l>r) return; a[rt].l=l; a[rt].r=r;
    21     if (l==r){
    22         val[rt]=sum[rt]=n[l]; return;
    23     }
    24     int mid=(l+r)>>1;
    25     build(l,mid,rt<<1); build(mid+1,r,rt<<1|1);
    26     pushup(rt,r-mid);
    27 }
    28 
    29 void update(int l,int r,int rt){
    30     if (l>r) return; 
    31     if (a[rt].l==l&&a[rt].r==r){
    32         opera(rt); return;
    33     }
    34     int mid=(a[rt].l+a[rt].r)>>1; pushdown(rt);
    35     if(r<=mid) update(l,r,rt<<1); 
    36         else if (l>mid) update(l,r,rt<<1|1);
    37         else update(l,mid,rt<<1),update(mid+1,r,rt<<1|1);  
    38     pushup(rt,a[rt].r-mid);
    39 }
    40 int query(){
    41     return (val[1]+bool(!sum[1]||flag))%p;
    42 }
    43 //------------------------------
    44 int main(){
    45     scanf("%s%d",s+1,&m); int len=strlen(s+1);
    46     for (int i=len;i>=1;--i) n[i]=s[i]-'0'; pw[0]=1;
    47     for (int i=1;i<=len;++i) pw[i]=pw[i-1]*2%p; 
    48     build(1,len-1,1); flag=n[len]; printf("%d
    ",query());
    49     while (m--){
    50         scanf("%d%d",&l,&r);
    51         if (l>r) swap(l,r);
    52         if (r==len) r--,flag^=1;
    53         update(l,r,1); printf("%d
    ",query());
    54     }
    55 }
    View Code

    总结:个人觉得这是一道很好的线段树题目,当然对于我这种蒟蒻,线段树还不能熟练掌握的还要加油

    ***虽说联赛不考

  • 相关阅读:
    21.算法实战---栈
    初见Gnuplot——时间序列的描述
    MATLAB连接MySQL数据库
    用python做些有意思的事——分析QQ聊天记录——私人订制
    遇见蒙特卡洛
    层次分析模型(AHP)及其MATLAB实现
    CPP,MATLAB实现牛顿插值
    CPP&MATLAB实现拉格朗日插值法
    python3爬虫再探之豆瓣影评数据抓取
    R——启程——豆瓣影评分析
  • 原文地址:https://www.cnblogs.com/logic-yzf/p/7603890.html
Copyright © 2020-2023  润新知