[JZOJ5924]【NOIP2018模擬10.23】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 還是不會做,他只能期待你能解決這個問題了。
對於 100% 的資料,滿足 0 ≤ n,m ≤ 10^5 ,1 ≤ a i ≤ n,1 ≤ l i ≤ r i ≤ n。
Solution
一般的資料結構比較難做,怎麼辦?
可以分塊!
對於每一塊我們維護一個連結串列,記錄每一塊連結串列的鏈頭和鏈尾。
查詢某個位置現在的標號我們可以先定位到塊,然後暴力找。
一個輪轉操作相當於將末尾拉出來插到前面去,為了維持塊大小恆定,我們還要講中間的塊的末尾移到下一塊的頭去。
對於每一塊開一個桶記錄答案。
查詢的時候兩邊的散點就暴力,中間的塊就直接在桶裡查。
複雜度
接下來講講比較不NOIP的做法…
我們先用一個splay維護輪轉操作,記好每次操作的左右端點的標號。
輪轉很麻煩,我們可以加入一些虛點,不妨將它們權值看成是0,輪轉操作就等於將末位置改成0,在頭前面修改。
這就變成了單點修改+區間統計答案。
帶修主席樹??
不需要。
將詢問和輪轉(因為每次只會變一個值)按照詢問的數分類,對於每一類都做一遍,這直接用樹狀陣列維護區間和即可。
複雜度
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);
}
}
}
}