1. 程式人生 > >[JZOJ5924]【NOIP2018模擬10.23】Queue

[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

一般的資料結構比較難做,怎麼辦?

可以分塊!

對於每一塊我們維護一個連結串列,記錄每一塊連結串列的鏈頭和鏈尾。
查詢某個位置現在的標號我們可以先定位到塊,然後暴力找。

一個輪轉操作相當於將末尾拉出來插到前面去,為了維持塊大小恆定,我們還要講中間的塊的末尾移到下一塊的頭去。

對於每一塊開一個桶記錄答案。
查詢的時候兩邊的散點就暴力,中間的塊就直接在桶裡查。

複雜度 O ( n

n ) O(n\sqrt n)

接下來講講比較不NOIP的做法…
我們先用一個splay維護輪轉操作,記好每次操作的左右端點的標號。

輪轉很麻煩,我們可以加入一些虛點,不妨將它們權值看成是0,輪轉操作就等於將末位置改成0,在頭前面修改。

這就變成了單點修改+區間統計答案。
帶修主席樹??

不需要。
將詢問和輪轉(因為每次只會變一個值)按照詢問的數分類,對於每一類都做一遍,這直接用樹狀陣列維護區間和即可。
複雜度 O

( n log n ) O(n\log n)

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);
			}
		}
	}
}