zoukankan      html  css  js  c++  java
  • 解题:由乃OI 2018 五彩斑斓的世界

    题面

    写在前面的扯淡:

    分块的总体学习告一段落,这算是分块集中学习的最后一题么;以后当然也可能会写,就是零零散散的题了=。=

    在洛谷上搜ynoi发现好像只有这道题和 由乃OI 2018 未来日记 是分块,久闻由乃OI之大名,就想试着写一写这道题(那道太毒了,写不了);结果一开始差点被吓跑了,不过最后还是硬着头皮写完了QAQ

    “分块最重要的就是常数” — —shadowice1984

    好像挺有道理的,分块从诞生之日起就是一个依靠你的决策而决定复杂度的算法(或思想),它不像什么高级的数据结构每块的写法都是基本固定的,复杂度有着严格的证明— —分块是个相对灵活的算法(所以也说它是一种思想)。举个例子:最简单的那种序列分块基本上复杂度都是$O(frac{n^2}{size})$,$size$就是你自己决定的块大小,大家可能都知道块大小开$sqrt(n)$(当然这是均值不等式算出来的),但是有时候可能某个操作的常数非常大,把块大小适当地调整可能会跑的更快......但是当卡常超过一定的地步,算法就又失去了本身的意义和美感......算了我语言表达能力太差了,还是赶快写题解吧=。=

    考虑到区间一个个查询修改非常慢,先用并查集把每块里相同的数字连到第一次出现的位置上,然后考虑修改操作$(l,r,v)$:

    对于一个最大值$maxx$不超过$2*v$的块,我们直接修改,即把大于$v$而小于最大值的部分的每个数$x$用并查集连到$x-v$

    对于一个最大值$maxx$大于$2*v$的块,我们反过来修改,把小于$v$的部分的每个数$x$连到$x+v$,同时在区间上打标记

    为什么要这样做?

    首先我们发现这两种修改方法本质是一样的

    然后我们发现对于第一种修改我们每次动的数是$v$级别的,同时我们把最大值缩小了$v$

    而对于第二种修改我们每次动的数是$maxx-v$级别的,在那个限制下其实还是$v$级别的,和上面的效率是一样的(注意我们是根据$maxx$分出两种情况的,这也是用到了分块的思想)

    然后均摊复杂度就有保证了,当然你还需要卡常(其实卡常不是很厉害,我就用了烂大街的快读+快输+register跑过去问题不大,还有这题当年在CF上好像因为CF太快+优化+3s实现把暴力放过去了=。=)

      1 #include<cmath>
      2 #include<cstdio>
      3 #include<cctype>
      4 #include<cstring>
      5 #include<algorithm>
      6 using namespace std;
      7 const int N=100005,Sq=320,M=100000;
      8 int a[N],ori[N],blo[N],ll[N],rr[N],aset[N];
      9 int siz[N],maxx[Sq],laz[Sq],firs[Sq][N];
     10 int n,m,t1,t2,t3,t4,sqr,cnt;
     11 inline int read()
     12 {
     13     int ret=0;
     14     char ch=getchar();
     15     while(!isdigit(ch))
     16         ch=getchar();
     17     while(isdigit(ch))
     18         ret=(ret<<3)+(ret<<1)+(ch^48),ch=getchar();
     19     return ret;    
     20 } 
     21 void write(int x)
     22 {
     23     if(x>9) write(x/10);
     24     putchar(x%10+48);
     25 }
     26 int finda(int x)
     27 {
     28     return (x==aset[x])?x:aset[x]=finda(aset[x]);
     29 }
     30 inline void rebuild(int b)//块重构 
     31 {
     32     register int i;
     33     for(i=ll[b];i<=rr[b];i++) 
     34     {
     35         maxx[b]=max(maxx[b],a[i]);
     36         firs[b][a[i]]=0,aset[i]=i,siz[i]=1;
     37     }
     38     for(i=ll[b];i<=rr[b];i++)//把每个数都并到第一次出现的位置 
     39     {
     40         int &fir=firs[b][a[i]];
     41         if(fir) siz[fir]+=siz[i],aset[i]=fir;
     42         else fir=i,ori[i]=a[i];
     43     }
     44 }
     45 inline void force(int b,int l,int r,int v)//大力重构 
     46 {
     47     register int i;
     48     for(i=ll[b];i<=rr[b];i++)
     49         firs[b][a[i]=ori[finda(i)]]=0;
     50     for(i=l;i<=r;i++)
     51         if(a[i]-laz[b]>v) a[i]-=v;
     52     rebuild(b);
     53 }
     54 void change(int l,int r,int v)
     55 {
     56     register int i,j;
     57     int b1=blo[l],b2=blo[r];
     58     if(b1!=b2)
     59     {
     60         force(b1,l,rr[b1],v);
     61         force(b2,ll[b2],r,v); 
     62         for(i=b1+1;i<=b2-1;i++)
     63             if(maxx[i]-laz[i]<2*v)//当最大值不超过2*v时,正常地修改 
     64             {
     65                 for(j=laz[i]+v+1;j<=maxx[i];j++)
     66                 {
     67                     int &pos1=firs[i][j];
     68                     int &pos2=firs[i][j-v];
     69                     if(pos1)
     70                     {
     71                         if(pos2) siz[pos2]+=siz[pos1],aset[pos1]=pos2;
     72                         else pos2=pos1,ori[pos2]=j-v; pos1=0;
     73                     }
     74                 }
     75                 maxx[i]=min(maxx[i],laz[i]+v);
     76             }
     77             else//否则反过来,把其余的数加上这个值并打标记
     78             {
     79                 for(j=laz[i]+1;j<=laz[i]+v;j++)
     80                 {
     81                     int &pos1=firs[i][j];
     82                     int &pos2=firs[i][j+v];
     83                     if(pos1)
     84                     {
     85                         if(pos2) siz[pos2]+=siz[pos1],aset[pos1]=pos2;
     86                         else pos2=pos1,ori[pos2]=j+v; pos1=0;
     87                     }
     88                 }
     89                 laz[i]+=v;
     90             }
     91     }
     92     else force(b1,l,r,v);
     93     //修改的理论依据:对于第一种情况最大值在O(v)时间内减小了v,对于第二种情况最大值在O(max-v)减少了max-v,所以最终的均摊复杂度是O(1)的 
     94     //(虽然CF的官方题解这里写的是极差,但我觉得不太对,例如对233,235两个数来说,将大于234的数减去234后极差反而在增大) 
     95 }
     96 int query(int l,int r,int v)//普通的查询 
     97 {
     98     register int i;
     99     int b1=blo[l],b2=blo[r],ret=0;
    100     if(b1!=b2)
    101     {
    102         for(i=l;i<=rr[b1];i++)
    103             ret+=(ori[finda(i)]-laz[b1]==v);
    104         for(i=ll[b2];i<=r;i++)
    105             ret+=(ori[finda(i)]-laz[b2]==v);
    106         for(i=b1+1;i<=b2-1;i++)
    107             if(laz[i]+v<=M) ret+=siz[firs[i][laz[i]+v]];
    108     }
    109     else
    110         for(i=l;i<=r;i++)
    111             ret+=(ori[finda(i)]-laz[b1]==v);
    112     return ret;
    113 }
    114 int main ()
    115 {
    116     register int i;
    117     n=read(),m=read();
    118     sqr=sqrt(n)+5,ll[cnt=1]=1;
    119     for(i=1;i<=n;i++)
    120     {
    121         a[i]=read(),ori[i]=a[i];
    122         aset[i]=i,blo[i]=(i-1)/sqr+1;
    123         maxx[blo[i]]=max(maxx[blo[i]],a[i]);
    124         if(i%sqr==0) rr[cnt++]=i,ll[cnt]=i+1; 
    125     }
    126     rr[cnt]=n;
    127     for(i=1;i<=cnt;i++) rebuild(i);
    128     for(i=1;i<=m;i++)
    129     {
    130         t1=read(),t2=read(),t3=read(),t4=read();
    131         if(t1==1) change(t2,t3,t4); else printf("%d
    ",query(t2,t3,t4));
    132     } 
    133     return 0;
    134 }
    View Code
  • 相关阅读:
    matlab矩阵中如何去掉重复的行;如何找到相同的行,并找到其位置
    Coursera 机器学习 第9章(下) Recommender Systems 学习笔记
    机器学习基石笔记1——在何时可以使用机器学习(1)
    Coursera 机器学习 第9章(上) Anomaly Detection 学习笔记
    matlab安装过程的被要求的配置程序
    jdk环境变量配置
    Coursera 机器学习 第8章(下) Dimensionality Reduction 学习笔记
    Coursera 机器学习 第8章(上) Unsupervised Learning 学习笔记
    Coursera 机器学习 第7章 Support Vector Machines 学习笔记
    linux服务器---squid限制
  • 原文地址:https://www.cnblogs.com/ydnhaha/p/9963914.html
Copyright © 2011-2022 走看看