[分块] Jzoj P5924 Queue

Description

         Hack 国的居民人人都是 OI 大师,Hometown 得知便赶紧来到 Hack 国学习。可想要进入 Hack 国并不是件容易的事情,首先就必须通过 Hack 国海关小 B 的考验。小 B 觉得 Hometown 比较菜,于是就扔了一道小水题给 Hometown。
        给定一个长度为 n 的数列 a i ,接下来会对这个序列进行 m 次操作。操作类型分为以下两种:
        • 1 l r,表示将区间 [l,r] 轮转一次,具体来说,a l ,a l+1 ,a l+2 ,··· ,a r−1 ,a r 经过一次轮转之后,会变为 a r ,a l ,a l+1 ,··· ,a r−1 ;
        • 2 l r k,询问区间 [l,r] 内 a i = k 的个数。
        可惜 Hometown 还是不会做,他只能期待你能解决这个问题了。

 

Input

从文件queue.in中读入数据。
第一行两个整数 n,m,表示序列的长度与操作的次数。
第二行 n 个整数 a i ,表示这个序列。
接下来的 m 行,每行先是一个整数 opt 表示操作的类型。对于 opt = 1 的操作,接下来两个整数 l,r 表示将区间 [l,r] 轮转;对于 opt = 2 的操作,接下来三个整数 l,r,k 表示求区间 [l,r] 内等于 k 的值的个数。

Output

输出到文件queue.out中。
对于每个 2 操作,一行一个整数,表示这次询问的答案。
 

Sample Input

7 6
1 2 2 3 2 1 3
2 3 6 2
1 1 6
2 2 4 1
1 3 6
2 6 7 3
2 3 5 2


Sample 2
见选手目录下的queue/queue2.in与queue/queue2.ans。
该组样例的数据范围同第 2 个测试点。

Sample 3
见选手目录下的queue/queue3.in与queue/queue3.ans。
该组样例的数据范围同第 13 个测试点。

Sample Output

2
1
2
3

Explanation
对于第一次询问,区间 [3,6] 中一共出现了 2 次 2。
随后进行修改,修改之后序列变为 1,1,2,2,3,2,3。
对于第二次询问,区间 [2,4] 中一共出现了 1 次 1。
随后再次修改,修改之后序列变为 1,1,2,2,2,3,3。
对于第三次询问,区间 [6,7] 中一共出现了 2 次 3。
对于第四次询问,区间 [3,5] 中一共出现了 3 次 2。
 

Data Constraint

对于 100% 的数据,满足 0 ≤ n,m ≤ 10^5 ,1 ≤ a i ≤ n,1 ≤ l i ≤ r i ≤ n。除此之外,对于每个数据点,还满足以下限制。


 

题解

  • 可以考虑分块,只用维护块内每个颜色出现的次数
  • 轮转的话,就将被包含的块的最后一个数放到下一个块的块首,再将最后一个数往前放
  • 查询的话,整块直接求,散块暴力求

代码

 1 #include <cstdio> 
 2 #include <iostream>
 3 #include <cmath>
 4 #define N 200010
 5 using namespace std;
 6 int n,m,num,l,a[N],pre[N],next[N],cnt[450][N],k[N],d[505],last[505];
 7 int find(int x)
 8 {
 9     int y=last[k[x]],w=min(n,num*k[x]);
10     while (w>x) y=pre[y],w--;
11     return y;
12 }
13 void del(int x,int y)
14 {
15     if (!x) return;
16     if (pre[x]) next[pre[x]]=next[x];
17     if (next[x]) pre[next[x]]=pre[x];
18     cnt[y][a[x]]--;
19     if (!pre[x]) d[y]=next[x];
20     if (!next[x]) last[y]=pre[x];
21 }
22 void add(int x,int y,int z)
23 {
24     if (!x) return;
25     next[pre[x]=pre[y]]=x,pre[next[x]=y]=x;
26     pre[0]=next[0]=0,cnt[z][a[x]]++;
27     if (!pre[x]) d[z]=x;
28     if (!next[x]) last[z]=x;
29 }
30 int main()
31 {
32     freopen("queue.in","r",stdin),freopen("queue.out","w",stdout);
33     scanf("%d%d",&n,&m);
34     if (n==0) return 0;
35     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
36     num=n/sqrt(n),k[0]=1;
37     for (int i=1;i<=n;i++)
38     {    
39         pre[i]=i-1,next[i]=i+1,l++;
40         if (l>num)
41         {
42             l=1;
43             k[i]=k[i-1]+1,last[k[i-1]]=i-1;
44             d[k[i]]=i,pre[i]=next[i-1]=0;
45         }
46         else k[i]=k[i-1];    
47         cnt[k[i]][a[i]]++; 
48     }    
49     next[n]=0,last[k[n]]=n,d[1]=1;
50     for (int i=1,l,r,op,z;i<=m;i++)
51     {
52         scanf("%d%d%d",&op,&l,&r);
53         if (l==r) continue;
54         if (op==1)
55         {
56             int x=find(l),y=find(r);
57             del(y,k[r]),add(y,x,k[l]);
58             for (int j=k[l];j<=k[r]-1;j++) 
59             {
60                 int p=last[j];
61                 del(p,j),add(p,d[j+1],j+1);
62             }
63         }
64         else
65         {
66             scanf("%d",&z);
67             int x=find(l),y=find(r),ans=0;
68             while (x&&x!=y) ans+=(a[x]==z),x=next[x];
69             while (y&&x!=y) ans+=(a[y]==z),y=pre[y];
70             if (x==y&&x!=0) ans+=(a[x]==z); else for (int j=k[l]+1;j<=k[r]-1;j++) ans+=cnt[j][z];
71             printf("%d\n",ans);
72         }
73     }
74     
75 }

 

posted @ 2018-10-24 15:28  BEYang_Z  阅读(181)  评论(0编辑  收藏  举报