• 菜鸡的 分块 刷题记录


    洛谷P2801 教主的魔法

    • 题意:区间加,然后查询区间内大于a的数.
    • 做法:对于每个块维护一个它的排好序的序列,每次查找lowerbound就星,不是整块的就暴力。

               然后注意每次加的时候不是整块的那部分原有顺序会被破坏,暴力再排序就是。

    • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 1000005
     3 
     4 using namespace std;
     5 int a[nmax],b[nmax],p[nmax],add[nmax]={0};
     6 int n,bn,m,q;
     7 
     8 inline void reset(int x){ //把块x的排序
     9     int r=bn*x,l=bn*(x-1)+1;
    10     for (int i=l; i<=r; i++) b[i]=a[i];
    11     sort(b+l,b+r+1);
    12 }
    13 
    14 void build(){
    15     cin>>n>>q;
    16     bn=sqrt(n);
    17     m=n/bn;
    18     if(n%bn) m++;
    19     for (int i=0; i<n; i++){
    20         scanf("%d",&a[i+1]);
    21         p[i+1]=i/bn+1;
    22     }
    23     for (int i=1; i<=m; i++) reset(i);
    24 }
    25 
    26 inline int find(int l,int r,int x){
    27     int ans=0;
    28     for (int i=l; i<=r; i++) if(a[i]>=x) ans++;
    29     return ans;
    30 }
    31 
    32 inline void update(int l,int r,int x){
    33     if(p[l]==p[r]) {
    34             for (int i=l; i<=r; i++) a[i]+=x;
    35             reset(p[l]);
    36             return;
    37     }
    38     for (int i=p[l]+1; i<p[r]; i++) add[i]+=x;
    39     for (int i=l; i<=p[l]*bn; i++) a[i]+=x;
    40     reset(p[l]);
    41     for (int i=(p[r]-1)*bn+1; i<=r; i++) a[i]+=x;
    42     reset(p[r]);
    43 }
    44 
    45 inline int query(int l,int r,int x){
    46     int ta,ans=0;
    47     if(p[l]==p[r]) return find(l,r,x-add[ p[l] ]);
    48     for (int i=p[l]+1; i<p[r]; i++) {
    49         int r=bn*i,l=bn*(i-1)+1;
    50         ta=lower_bound(b+l,b+r+1,x-add[i])-(b+l);
    51         ans+=bn-ta;
    52     }
    53     ans+=find( l,p[l]*bn,x-add[ p[l] ]);
    54     ans+=find( (p[r]-1)*bn+1 ,r,x-add[ p[r] ]);
    55     return ans;
    56 }
    57 
    58 int main(){
    59     freopen("testdata.in","r",stdin);
    60     build();
    61     char in[10];
    62     int l,r,t;
    63     for (int i=0; i<q; i++) {
    64         scanf("%s%d%d%d",in,&l,&r,&t);
    65         if(in[0]=='M') update(l,r,t);
    66         else printf("%d
    ",query(l,r,t));
    67     }
    68     return 0;
    69 }
    View Code
    • 基本上照着hzwer学长打的,orz感谢这么好的分块教程。

     

    BZOJ2120  数颜色

    • 做法:用pre[i]表示下标为i的那个颜色它前一个同样颜色的下标,如果要统计l~r的颜色,则对于pre[i]>l的不计数,因为在l~r内这个颜色一定出现过不止一次了,然后就跟上面教主的魔法一样了~
    • 注意:这道题能直接分块,修改的时候完全暴力,是因为有 “修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6” 这两个条件,不然就得换做法了quq。
    • 代码:
       1 #include <bits/stdc++.h>
       2 #define nmax 10010
       3 #define inf 1000010
       4 
       5 using namespace std;
       6 int n,q,m,bn;
       7 int a[nmax],b[nmax],p[nmax],pre[nmax],idx[nmax*100];
       8 
       9 inline void reset(int x){
      10     int l=(x-1)*bn+1,r=x*bn;
      11     for (int i=l; i<=r; i++) b[i]=pre[i];
      12     sort(b+l,b+r+1);
      13 }
      14 
      15 inline int find(int l,int r,int ll){
      16     int ans=0;
      17     for (int i=l; i<=r; i++) if(pre[i]<ll) ans++;
      18     return ans;
      19 }
      20 
      21 void build(){
      22     cin>>n>>q;
      23     bn=sqrt(n);
      24     m=n/bn;
      25     if(n%bn) m++;
      26     for (int i=1; i<=n; i++) {
      27         scanf("%d",&a[i]);
      28         int& t=idx[ a[i] ];
      29         pre[i]=t;
      30         t=i;
      31         p[i]=(i-1)/bn+1;
      32     }
      33     for (int i=n+1; i<=m*bn; i++) pre[i]=inf;
      34     for (int i=1; i<=m; i++) reset(i);
      35 }
      36 
      37 inline void update(int c,int d){
      38     bool change=false;
      39     for (int i=c-1; i>=1; i--) if(a[i]==d) { pre[c]=i; reset(p[c]); change=true; break; }
      40     for (int i=c+1; i<=n; i++) if(a[i]==d) { pre[i]=c; reset(p[i]); break; }
      41     for (int i=c+1; i<=n; i++) if(a[i]==a[c]) {pre[i]=pre[c]; reset(p[i]); break;}
      42     if(!change) pre[c]=0; reset(p[c]);
      43     a[c]=d;
      44 }
      45 
      46 inline int query(int l,int r){
      47     int ans=0;
      48     if(p[l]==p[r]) ans=find(l,r,l);
      49     else{
      50         for (int i=p[l]+1; i<p[r]; i++) {
      51             int tl=(i-1)*bn+1,tr=i*bn;
      52             int t=lower_bound(b+tl,b+tr+1,l)-(b+tl);
      53             ans+=t;
      54         }
      55         ans+=find(l,p[l]*bn,l);
      56         ans+=find( (p[r]-1)*bn+1 ,r,l);
      57     }
      58     return ans;
      59 }
      60 
      61 int main(){
      62     build();
      63     char in[10];
      64     int c,d;
      65     for (int i=0; i<q; i++) {
      66         scanf("%s%d%d",in,&c,&d);
      67         if(in[0]=='Q') printf("%d
      ",query(c,d));
      68         else update(c,d);
      69     }
      70     return 0;
      71 }
      View Code

     

    BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊

    • 这题用LCT就很难(因为不会),但是用分块就是煞笔题
    • 维护每个点跳出自己那个块需要的次数和跳到的地方就ok了,见代码
    • 代码:
       1 #include <bits/stdc++.h>
       2 #define nmax 400020
       3 
       4 using namespace std;
       5 int a[nmax],pos[nmax],jump[nmax],cnt[nmax];
       6 //jump[i]表示点跳出i那个块跳到的地方
       7 //cnt[i]表示跳出i那个块需要的次数
       8 int n,m,bn,next,q;
       9 
      10 inline void work(int idx){
      11     next=idx+a[idx];
      12     cnt[idx]=1;
      13     jump[idx]=next;
      14     if(pos[next]==pos[idx]) { cnt[idx]+=cnt[next]; jump[idx]=jump[next]; }
      15 }
      16 
      17 void build(){
      18     bn=sqrt(n);
      19     m=n/bn;
      20     if(n%bn) m++;
      21     for (int i=0; i<n; i++) pos[i]=(i/bn);
      22     for (int i=n; i<n+2*bn; i++) pos[i]=m+1;
      23     for (int j=n-1; j>=0; j--) work(j);
      24 }
      25 
      26 void change(int p,int x){
      27     int l=pos[p]*bn;
      28     a[p]=x;
      29     for (int i=p; i>=l; i--) work(i);
      30 }
      31 
      32 int solve(int p){
      33     int ans=0;
      34     while(p<n){
      35         ans+=cnt[p];
      36         p=jump[p];
      37     }
      38     return ans;
      39 }
      40 
      41 int main(){
      42     cin>>n;
      43     for (int i=0; i<n; i++) scanf("%d",&a[i]);
      44     build();
      45     cin>>q;
      46     int ina,inb,inc;
      47     while(q--){
      48         scanf("%d%d",&ina,&inb);
      49         if(ina==1) printf("%d
      ",solve(inb));
      50         else{
      51             scanf("%d",&inc);
      52             change(inb,inc);
      53         }
      54     }
      55     return 0;
      56 }
      ┗|`O′|┛
  • 相关阅读:
    ZipArchive 的使用
    Bootstrap使用心得
    SQL SERVER 级联删除
    ASP.NET 使用C#代码设置页面元素中的样式或属性
    GDI+中发生一般性错误之文件被占用
    .Net 中资源的使用方式
    一张图全解析个性化邮件那么重要
    看天猫EDM营销学企业EDM营销
    细数EDM营销中存在的两大盲点
    如何进行EDM邮件内容的撰写
  • 原文地址:https://www.cnblogs.com/jiecaoer/p/11255742.html
Copyright © 2020-2023  润新知