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 还是不会做,他只能期待你能解决这个问题了。
对于 100% 的数据,满足 0 ≤ n,m ≤ 10^5 ,1 ≤ a i ≤ n,1 ≤ l i ≤ r i ≤ n。

Solution

一般的数据结构比较难做,怎么办?

可以分块!

对于每一块我们维护一个链表,记录每一块链表的链头和链尾。
查询某个位置现在的标号我们可以先定位到块,然后暴力找。

一个轮转操作相当于将末尾拉出来插到前面去,为了维持块大小恒定,我们还要讲中间的块的末尾移到下一块的头去。

对于每一块开一个桶记录答案。
查询的时候两边的散点就暴力,中间的块就直接在桶里查。

复杂度[JZOJ5924]【NOIP2018模拟10.23】Queue_树状数组

接下来讲讲比较不NOIP的做法…
我们先用一个splay维护轮转操作,记好每次操作的左右端点的标号。

轮转很麻烦,我们可以加入一些虚点,不妨将它们权值看成是0,轮转操作就等于将末位置改成0,在头前面修改。

这就变成了单点修改+区间统计答案。
带修主席树??

不需要。
将询问和轮转(因为每次只会变一个值)按照询问的数分类,对于每一类都做一遍,这直接用树状数组维护区间和即可。
复杂度[JZOJ5924]【NOIP2018模拟10.23】Queue_分块_02

Code

#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
using namespace std;
int n,m,a[N],pre[N],nxt[N],cnt[450][N],fr[N],n1,fi[505],ls[505];
int wz(int x)
{
int l=ls[fr[x]],c=min(n,fr[x]*n1);
while(c>x) l=pre[l],c--;
return l;
}
void del(int k,int w)
{
if(!k) return;
if(pre[k]) nxt[pre[k]]=nxt[k];
if(nxt[k]) pre[nxt[k]]=pre[k];
cnt[w][a[k]]--;
if(!pre[k]) fi[w]=nxt[k];
if(!nxt[k]) ls[w]=pre[k];
}
void ins(int k,int x,int w)
{
if(!k) return;
nxt[pre[k]=pre[x]]=k;
pre[nxt[k]=x]=k;
pre[0]=nxt[0]=0;
cnt[w][a[k]]++;
if(!pre[k]) fi[w]=k;
if(!nxt[k]) ls[w]=k;
}
void read(int &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
int main()
{
cin>>n>>m;
if(n==0) return 0;
fo(i,1,n) read(a[i]);
n1=n/sqrt(n);
int c1=0;
fr[0]=1;
fo(i,1,n)
{
pre[i]=i-1,nxt[i]=i+1;
c1++;
if(c1>n1) fr[i]=fr[i-1]+1,c1=1,ls[fr[i-1]]=i-1,fi[fr[i]]=i,pre[i]=nxt[i-1]=0;
else fr[i]=fr[i-1];
cnt[fr[i]][a[i]]++;
}
nxt[n]=0;
ls[fr[n]]=n;
fi[1]=1;
fo(i,1,m)
{
int p,x,y,z;
read(p),read(x),read(y);
if(x==y) continue;
if(p==2)
{
scanf("%d",&z);
int s=0,l=wz(x),r=wz(y);
while(l&&l!=r) s+=(a[l]==z),l=nxt[l];
while(r&&l!=r) s+=(a[r]==z),r=pre[r];
if(l==r&&l!=0) s+=(a[l]==z);
else fo(j,fr[x]+1,fr[y]-1) s+=cnt[j][z];
printf("%d\n",s);
}
else
{
int l=wz(x),r=wz(y);
del(r,fr[y]),ins(r,l,fr[x]);
fo(j,fr[x],fr[y]-1)
{
int p=ls[j];
del(p,j);
ins(p,fi[j+1],j+1);
}
}
}
}