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。除此之外,对于每个数据点,还满足以下限制。
Solution
-
这题我的方法是分块(也可以用 splay+权值线段树)。
-
用类似块状链表的方法,所有数用一个双向链表连起来。
-
并且每 n \sqrt n n 个数分成一块,每块记录开头指针和结尾指针。
-
之后每一块开一个桶存每个数出现的次数。
-
询问的话整块直接在桶中读取,散块直接扫。
-
如果是翻转的话呢,就把 a [ r ] a[r] a[r] 对应的指针剪下来,并将其插到 a [ l ] a[l] a[l] 对应的指针前。
-
中途顺带修改每个块的信息即可。
-
查询和修改的复杂度均为 O ( n ) O(\sqrt n) O(n) 。
-
总时间复杂度 O ( n n ) O(n\sqrt n) O(nn) 。
-
要注意下细节和特殊情况,比如说 n = 0 n=0 n=0 、 l , r l,r l,r 相同或在同一块内……
Code
#include<cstdio>
#include<cmath>
#include<cctype>
using namespace std;
const int N=1e5+5;
int siz,tot;
int a[N],t[318][N],lt[N],nex[N];
int id[N],st[N],en[N],pl[N],pr[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int main()
{
freopen("queue.in","r",stdin);
freopen("queue.out","w",stdout);
int n=read(),m=read();
if(!n) return 0;
siz=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) nex[i]=i+1,lt[i]=i-1;
int sum=0;
while(sum<n)
{
tot++;
pl[tot]=st[tot]=sum+1;
for(int i=1;i<=siz && sum<n;i++)
{
sum++;
t[tot][a[sum]]++;
id[sum]=tot;
}
pr[tot]=en[tot]=sum;
}
while(m--)
{
int op=read(),l=read(),r=read();
if(op==1)
{
if(l==r) continue;
int ll=(l-1)/siz+1,rr=(r-1)/siz+1;
int posr=pr[rr]==r?en[rr]:0;
if(!posr)
{
posr=st[rr];
for(int i=pl[rr];i<r;i++) posr=nex[posr];
}
int posl=pl[ll]==l?st[ll]:0;
if(!posl)
{
posl=en[ll];
for(int i=pr[ll];i>l;i--) posl=lt[posl];
}
t[id[posr]][a[posr]]--;
nex[lt[posr]]=nex[posr];
lt[nex[posr]]=lt[posr];
if(en[rr]==posr) en[rr]=lt[posr];
int pos=posr;
bool pd=false;
while(id[posl]^id[pos])
{
pos=st[id[pos]];
st[id[pos]]=lt[pos];
pos=lt[pos];
if(pos==posl)
{
en[id[pos]]=posr;
id[posr]=id[posl];
t[id[pos]][a[pos]]--;
id[pos]++;
t[id[pos]][a[pos]]++;
pd=true;
break;
}
en[id[pos]]=lt[pos];
t[id[pos]][a[pos]]--;
id[pos]++;
t[id[pos]][a[pos]]++;
pos=lt[pos];
}
if(st[ll]==posl) st[ll]=posr;
if(!pd) id[posr]=id[posl];
nex[lt[posl]]=posr;
lt[posr]=lt[posl];
nex[posr]=posl;
lt[posl]=posr;
t[id[posr]][a[posr]]++;
}else
{
int k=read(),ans=0;
int ll=(l-1)/siz+1,rr=(r-1)/siz+1;
if(ll==rr)
{
int pos=en[ll];
for(int i=pr[ll];i>r;i--) pos=lt[pos];
for(int i=r;i>=l;i--)
{
if(a[pos]==k) ans++;
pos=lt[pos];
}
write(ans),putchar('\n');
continue;
}
if(pl[ll]^l)
{
int pos=en[ll];
for(int i=pr[ll];i>=l;i--)
{
if(a[pos]==k) ans++;
pos=lt[pos];
}
ll++;
}
if(pr[rr]^r)
{
int pos=st[rr];
for(int i=pl[rr];i<=r;i++)
{
if(a[pos]==k) ans++;
pos=nex[pos];
}
rr--;
}
for(int i=ll;i<=rr;i++) ans+=t[i][k];
write(ans),putchar('\n');
}
}
return 0;
}