【JZOJ5924】Queue

Description

给一个序列,支持区间轮换(即末尾的一个数挪到开头),查询区间等于k的有多少个。

Solution

分块,记 s i , j s_{i,j} si,j为第 i i i j j j出现的次数,每个块维护一个链表,修改就是对 O ( n ) O(\sqrt n) O(n )的块进行修改,用deque实现非常方便。
当然也可以打非旋转Treap,每个点维护权值线段树,合并的时候向上线段树合并。
也有离线做法,先用splay模拟一遍,把空位建出来,后按权值分类后用树状数组求即可。

Code

分块大法好

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<deque>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
const int N=1e5+10,_N=350;
int be[N],tt=0;
int L[N],R[N];
int s[_N][N];
deque<int> b[N];
int c[N],t=0;
int a[N];
int main()
{
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout);
	int n,m,_n;
	scanf("%d %d",&n,&m);
	for(_n=1;_n*_n<=n;_n++);
	int t=0,tt=1;
	L[1]=1;
	fo(i,1,n){
		scanf("%d",&a[i]);
		be[i]=tt;
		if(++t==_n) t=0,R[tt]=i,L[++tt]=i+1; 
	}
	R[tt]=n;
	fo(i,1,tt)
	fo(j,L[i],R[i]) s[i][a[j]]++,b[i].push_back(a[j]);
	while(m--){
		int op,l,r;
		scanf("%d %d %d",&op,&l,&r);
		if(op==1){
			if(be[l]==be[r]){
				int p=be[l],w=b[p].at(r-L[p]);
				b[p].erase(b[p].begin()+r-L[p]);
				b[p].insert(b[p].begin()+l-L[p],w);
				continue;
			}
			int p=be[l],q=be[r];
			int w=b[q].at(r-L[q]);
			b[q].erase(b[q].begin()+r-L[q]),s[q][w]--;
			b[p].insert(b[p].begin()+l-L[p],w),s[p][w]++;
			fo(i,p,q-1){
				int w=b[i].back();
				b[i].pop_back(),b[i+1].push_front(w);
				s[i][w]--,s[i+1][w]++;
			}
		}
		else{
			int k;
			scanf("%d",&k);
			int ans=0;
			if(be[l]==be[r]){
				int p=be[l];
				fo(i,l,r) ans+=b[p].at(i-L[p])==k;
				printf("%d\n",ans);
				continue;
			}
			int p=be[l],q=be[r];
			fo(i,l,R[p]) ans+=b[p].at(i-L[p])==k;
			fo(i,L[q],r) ans+=b[q].at(i-L[q])==k;
			fo(i,p+1,q-1) ans+=s[i][k];
			printf("%d\n",ans);
		}
	}
}

posted @ 2018-10-24 21:52  sadstone  阅读(45)  评论(0编辑  收藏  举报