[JZOJ5924]【NOIP2018模拟10.23】Queue

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83310347

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

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83310347
今日推荐