菜鸡的 分块 刷题记录

洛谷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\n",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\n",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\n",solve(inb));
    50         else{
    51             scanf("%d",&inc);
    52             change(inb,inc);
    53         }
    54     }
    55     return 0;
    56 }
    ┗|`O′|┛

     

posted @ 2019-07-28 19:25  连昵称都不能重复  阅读(220)  评论(0编辑  收藏  举报