• JZOJ.5289【NOIP2017模拟8.17】偷笑


    Description

    berber走进机房,边敲门边喊:“我是哔哔”
    CRAZY转过头:“我警告你,哔哔刚刚来过!”
    “呵呵呵呵……”
    这时,哔哔站了起来,环顾四周:“你们笑什么?……”
    巧了,发出笑声的人都排成了一排,每个人刚开始发出的笑声值为a[i]的笑声。但是有些笑声哔哔是听不出来的,他只听得出笑声值只包含2和3的数字,比如说什么2333。
    但是同学们还是很会秀操作的。对于操作add  l r x表示l到r的同学的笑声值同时加上x。数据保证操作完的x在2*10^4以内。
    但是,哔哔还是很想知道哪些人在笑。对于count l r表示询问l到r的同学中有多少个笑声值是哔哔听得出来的(就是只由2和3组成)。
    哔哔是很忙的,他需要坐在旁边的你来帮他完成这个问题。
     

    Input

    第一行两个整数分别为n,m表示同学的数目和操作数。第二行n个整数a[i]表示同学一开始的笑声值。接下来m行表示操作数,描述如上。

    Output

    按照题目输出答案。
     

    Sample Input

    3 6
    2 3 4
    count 1 3
    count 1 2
    add 1 3 2
    count 1 3
    add 2 3 3
    count 1 3

    Sample Output

    2
    2
    0
    0
     

    Data Constraint

    对于20%的数据n,m≤2000
    对于另外30%的数据n,m≤50000
    对于另外50%的数据n,m≤300000
    本题时限3s

    题意就是区间修改和区间查询,其中查询是某区间内不含2,3以外的数的个数。

    区间修改我们可以用线段树来维护,但问题就在查询。线段树查询并不支持直接查询题目要求的数的个数,我们虽然可以一个一个检查,但除法取模运算相比也比较慢,每次查询的nlogn复杂度也接受不了。

    直接维护不好做,我们可以尝试改变一下线段树维护的信息。

    我们注意到题目给出的a[i]的最大值不会超过20000,在20000内符合题目的233数一共也只有31个,我们可以预处理这些,然后维护每个数a[i]与这些数相差最小的且比a[i]大的数的差值,很明显,当差值为0的时候就说明它是个233数,如果差值小于0,我们就要找下一个比它大且相差最小的233数,作差比较,由于每个数最多被修改31次,所以复杂度为O(31mlogn)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<ctime>
     5 #define N 1600000
     6 using namespace std;
     7 struct data{
     8     int cnt,min,mark;
     9 }tree[N];
    10 int f[32]={2,3,22,23,32,33,222,223,232,233,322,323,332,333,2222,2223,2232,2233,2322,2323,2332,2333,3222,3223,3232,3233,3322,3323,3332,3333,22222};
    11 int n,m,voi[N/4],x,y,z,top[N/4];
    12 char str[10];
    13 void build(int root,int l,int r){
    14     tree[root].mark=0;
    15     if (l==r){
    16         while (f[top[l]]<voi[l]) top[l]++;
    17         tree[root].min=f[top[l]]-voi[l];
    18         if (tree[root].min==0) tree[root].cnt=1;
    19         return;
    20     }
    21     int mid=(l+r)>>1;
    22     build(root<<1,l,mid);
    23     build(root<<1|1,mid+1,r);
    24     tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min);
    25     tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt;
    26 }
    27 void pushdown(int root,int l,int r){
    28     if (tree[root].mark){
    29         tree[root<<1].mark+=tree[root].mark;
    30         tree[root<<1|1].mark+=tree[root].mark;
    31         tree[root<<1].min+=tree[root].mark;
    32         tree[root<<1|1].min+=tree[root].mark;
    33         tree[root].mark=0;
    34     }
    35     return;
    36 }
    37 void updata(int root,int l,int r){
    38     if (tree[root].min>0) return;
    39     if (l==r){
    40         if (tree[root].min<0){
    41             voi[l]-=tree[root].mark;
    42             tree[root].mark=0;
    43             while (f[top[l]]<voi[l]) top[l]++;
    44             tree[root].min=f[top[l]]-voi[l];
    45         }
    46         if (tree[root].min==0) tree[root].cnt=1;
    47         else tree[root].cnt=0;
    48         return;
    49     }
    50     pushdown(root,l,r);
    51     int mid=(l+r)>>1;
    52     updata(root<<1,l,mid);
    53     updata(root<<1|1,mid+1,r);
    54     tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min);
    55     tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt;
    56 }
    57 void add(int root,int l,int r,int x,int y,int z){
    58     if ((x<=l)&&(y>=r)){
    59         tree[root].min+=z;
    60         tree[root].mark+=z;
    61         if (tree[root].min<=0)
    62             updata(root,l,r);
    63         return;
    64     }
    65     pushdown(root,l,r);
    66     int mid=(l+r)>>1;
    67     if (x<=mid) add(root<<1,l,mid,x,y,z);
    68     if (y>mid) add(root<<1|1,mid+1,r,x,y,z);
    69     tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min);
    70     tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt;
    71 }
    72 int count(int root,int l,int r,int x,int y){
    73     if ((x<=l)&&(y>=r)) return tree[root].cnt;
    74     int ans=0;
    75     int mid=(l+r)>>1;
    76     if (x<=mid) ans+=count(root<<1,l,mid,x,y);
    77     if (y>mid) ans+=count(root<<1|1,mid+1,r,x,y);
    78     return ans; 
    79 }
    80 int main(){
    81     scanf("%d%d",&n,&m);
    82     for (int i=1;i<=n;i++)
    83      scanf("%d",&voi[i]);
    84     build(1,1,n);
    85     while (m--){
    86         scanf("%s",str);
    87         if (str[0]=='c'){
    88             scanf("%d%d",&x,&y);
    89             printf("%d
    ",count(1,1,n,x,y));
    90         }
    91         else{
    92             scanf("%d%d%d",&x,&y,&z);
    93             add(1,1,n,x,y,-z);
    94         }
    95     }
    96     return 0;
    97 }
    神奇的代码

    (cin只读入字符串还是巨慢QAQ改成scanf瞬间从3000ms+变成300ms+)

  • 相关阅读:
    动画 与 缓存
    flex 弹性布局
    JavaWeb学习总结(二)——Tomcat服务器学习和使用(一)
    java web总结(1)----入门
    面试系列-java(1)
    面试英语——题外话------笔记(1)
    面试内容的概览(1)-笔记
    菜鸟教程-设计模式简介-笔记
    java基础知识回顾-笔记(微学苑)
    mysql学习总结-笔记(总结的不怎么样)
  • 原文地址:https://www.cnblogs.com/Lanly/p/7387541.html
Copyright © 2020-2023  润新知