JZOJ 5924. 【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 还是不会做,他只能期待你能解决这个问题了。
Description

Input

从文件queue.in中读入数据。
第一行两个整数 n,m,表示序列的长度与操作的次数。
第二行 n 个整数 a i ,表示这个序列。
接下来的 m 行,每行先是一个整数 opt 表示操作的类型。对于 opt = 1 的操作,接下来两个整数 l,r 表示将区间 [l,r] 轮转;对于 opt = 2 的操作,接下来三个整数 l,r,k 表示求区间 [l,r] 内等于 k 的值的个数。

Output

输出到文件queue.out中。
对于每个 2 操作,一行一个整数,表示这次询问的答案。

Sample Input

7 6
1 2 2 3 2 1 3
2 3 6 2
1 1 6
2 2 4 1
1 3 6
2 6 7 3
2 3 5 2

Sample 2

见选手目录下的queue/queue2.in与queue/queue2.ans。
该组样例的数据范围同第 2 个测试点。

Sample 3

见选手目录下的queue/queue3.in与queue/queue3.ans。
该组样例的数据范围同第 13 个测试点。

Sample Output

2
1
2
3

Explanation

对于第一次询问,区间 [3,6] 中一共出现了 2 次 2。
随后进行修改,修改之后序列变为 1,1,2,2,3,2,3。
对于第二次询问,区间 [2,4] 中一共出现了 1 次 1。
随后再次修改,修改之后序列变为 1,1,2,2,2,3,3。
对于第三次询问,区间 [6,7] 中一共出现了 2 次 3。
对于第四次询问,区间 [3,5] 中一共出现了 3 次 2。

Data Constraint

对于 100% 的数据,满足 0 ≤ n,m ≤ 10^5 ,1 ≤ a i ≤ n,1 ≤ l i ≤ r i ≤ n。除此之外,对于每个数据点,还满足以下限制。

Data Constraint
Data Constraint

Solution

  • 这题我的方法是分块(也可以用 splay+权值线段树)。

  • 用类似块状链表的方法,所有数用一个双向链表连起来。

  • 并且每 n \sqrt n n 个数分成一块,每块记录开头指针和结尾指针。

  • 之后每一块开一个桶存每个数出现的次数。

  • 询问的话整块直接在桶中读取,散块直接扫。

  • 如果是翻转的话呢,就把 a [ r ] a[r] a[r] 对应的指针剪下来,并将其插到 a [ l ] a[l] a[l] 对应的指针前。

  • 中途顺带修改每个块的信息即可。

  • 查询和修改的复杂度均为 O ( n ) O(\sqrt n) O(n )

  • 总时间复杂度 O ( n n ) O(n\sqrt n) O(nn )

  • 要注意下细节和特殊情况,比如说 n = 0 n=0 n=0 l , r l,r l,r 相同或在同一块内……

Code

#include<cstdio>
#include<cmath>
#include<cctype>
using namespace std;
const int N=1e5+5;
int siz,tot;
int a[N],t[318][N],lt[N],nex[N];
int id[N],st[N],en[N],pl[N],pr[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
void write(int x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int main()
{
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout);
	int n=read(),m=read();
	if(!n) return 0;
	siz=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) nex[i]=i+1,lt[i]=i-1;
	int sum=0;
	while(sum<n)
	{
		tot++;
		pl[tot]=st[tot]=sum+1;
		for(int i=1;i<=siz && sum<n;i++)
		{
			sum++;
			t[tot][a[sum]]++;
			id[sum]=tot;
		}
		pr[tot]=en[tot]=sum;
	}
	while(m--)
	{
		int op=read(),l=read(),r=read();
		if(op==1)
		{
			if(l==r) continue;
			int ll=(l-1)/siz+1,rr=(r-1)/siz+1;
			int posr=pr[rr]==r?en[rr]:0;
			if(!posr)
			{
				posr=st[rr];
				for(int i=pl[rr];i<r;i++) posr=nex[posr];
			}
			int posl=pl[ll]==l?st[ll]:0;
			if(!posl)
			{
				posl=en[ll];
				for(int i=pr[ll];i>l;i--) posl=lt[posl];
			}
			t[id[posr]][a[posr]]--;
			nex[lt[posr]]=nex[posr];
			lt[nex[posr]]=lt[posr];
			if(en[rr]==posr) en[rr]=lt[posr];
			int pos=posr;
			bool pd=false;
			while(id[posl]^id[pos])
			{
				pos=st[id[pos]];
				st[id[pos]]=lt[pos];
				pos=lt[pos];
				if(pos==posl)
				{
					en[id[pos]]=posr;
					id[posr]=id[posl];
					t[id[pos]][a[pos]]--;
					id[pos]++;
					t[id[pos]][a[pos]]++;
					pd=true;
					break;
				}
				en[id[pos]]=lt[pos];
				t[id[pos]][a[pos]]--;
				id[pos]++;
				t[id[pos]][a[pos]]++;
				pos=lt[pos];
			}
			if(st[ll]==posl) st[ll]=posr;
			if(!pd) id[posr]=id[posl];
			nex[lt[posl]]=posr;
			lt[posr]=lt[posl];
			nex[posr]=posl;
			lt[posl]=posr;
			t[id[posr]][a[posr]]++;
		}else
		{
			int k=read(),ans=0;
			int ll=(l-1)/siz+1,rr=(r-1)/siz+1;
			if(ll==rr)
			{
				int pos=en[ll];
				for(int i=pr[ll];i>r;i--) pos=lt[pos];
				for(int i=r;i>=l;i--)
				{
					if(a[pos]==k) ans++;
					pos=lt[pos];
				}
				write(ans),putchar('\n');
				continue;
			}
			if(pl[ll]^l)
			{
				int pos=en[ll];
				for(int i=pr[ll];i>=l;i--)
				{
					if(a[pos]==k) ans++;
					pos=lt[pos];
				}
				ll++;
			}
			if(pr[rr]^r)
			{
				int pos=st[rr];
				for(int i=pl[rr];i<=r;i++)
				{
					if(a[pos]==k) ans++;
					pos=nex[pos];
				}
				rr--;
			}
			for(int i=ll;i<=rr;i++) ans+=t[i][k];
			write(ans),putchar('\n');
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值