CF455D Serega and Fun 题解

Before it

来当一回教练的题解翻译家,平衡树这种高级算法自然是不会的,所以详细讲一下STL。

成为蒟蒻(也就是自己)的福音。

Solution

我们观察到题目要求“把第 \(r\) 个数插入在第 \(l\) 个数之前,其他数顺序不变“,使用 \(deque\)\(push\)_\(front\)\(push\) _ $ back$ 操作可以轻而易举的实现。

现在考虑分块,对于每一块维护一个\(deque\) ,$q_{i,j} $ 表示第 \(i\) 块,第 \(j\) 位的值是多少,同时对于每一块开一个桶数组,\(tot_{i,j}\) 维护第 \(i\) 块有多少个 \(j\)

这里详细讲一下如何处理操作。

  1. 初始化

    请参考数列分块9题,维护每个块的开头,结尾,以及 \(i\) 在哪个块。(作者使用 \(pos\) 数组。

  2. 移动

    对于在同一个块的情况,暴力修改即可。可以开一个栈辅助转移(当然啦你不开也可以。

    对于不在同一个块的情况,求出左碎块和右碎块的大小,我们先把左碎块的值存到栈里,然后将其直接删去。然后找到被往前移的那个加到栈里,然后再讲左碎块填满,此时栈里还剩一个元素。

    对于中间的整块,实际上也就是元素都往后移动一个,可以在这个块的 \(deque\) 数组里 \(push\)_ \(front\) 上一个块遗留下来的最后一个值,然后在把最后一个元素 \(pop\)_\(back\) 走,把它存到栈里转移到下一个块。

    对于右碎块,我们先把右碎块里的值依次存入栈里,扔掉栈顶元素(也就是被往前移动的那个),最后再依次将右碎块补满即可。

    注意在转移的过程中,注意 \(tot\) 也需要同步转移。

  3. 求出区间 \(l--r\)\(k\) 的值

    对于在同一个块的情况,直接暴力计算即可。

    对于左右碎块,求出长度和起终点,暴力计算。这里注意端点的细节!(作者写错这里调了很久...

    对于中间的块,计算桶的答案。

至此,本题得到解决。

时间复杂度 \(O(n\sqrt{n})\)。但是因为STL常数很大所以跑得很慢...

Code

代码
//分块题,并不是神秘线段树
//对于每个块维护一个deque  用来支持1操作
//维护一个桶,便于查询 
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,blen,a[maxn],op,l,r,k,s[maxn];//s数组是手写栈,为了方便转移操作的 
int pos[maxn],st[maxn],ed[maxn],tot[320][maxn];//tot[i][j]表示第i块有多少个j 
deque<int> q[320]; //和vector类似,可以使用下标访问 
void init(){
	blen=sqrt(n);
	int t=n/blen;
	if(n%blen)t++;
	for(int i=1;i<=t;i++){
		st[i]=ed[i-1]+1;
		ed[i]=ed[i-1]+blen;
	}
	ed[t]=n;
	for(int i=1;i<=n;i++){
		pos[i]=(i-1)/blen+1;
		tot[pos[i]][a[i]]++;
		q[pos[i]].push_back(a[i]);
	}
}
void modify(int l,int r){
	int top=0;
	if(pos[l]==pos[r]){
		int now=pos[l];
		l-=st[now],r-=st[now],s[++top]=q[now][r];
		for(int i=l;i<r;i++) s[++top]=q[now][i];
		for(int i=r;i>=l;i--) q[now][i]=s[top--];
		return;
	}
	int cnt1=ed[pos[l]]-l+1,cnt2=r-st[pos[r]]+1;
	while(cnt1--){
		int x=q[pos[l]].back();
		s[++top]=x;
		tot[pos[l]][x]--;
		q[pos[l]].pop_back();
	}
	s[++top]=q[pos[r]][cnt2-1];//被往前移的那个
	while(top>1){
		int x=s[top];
		q[pos[l]].push_back(x);
		tot[pos[l]][x]++;
		top--;
	} 
	for(int i=pos[l]+1;i<=pos[r]-1;i++){//处理整块 往后移动一个 
		int x=s[top];
		q[i].push_front(x);
		tot[i][x]++,top--;
		int y=q[i].back();
		tot[i][y]--;
		s[++top]=y;
		q[i].pop_back();
	}
	while(cnt2--){
		int x=q[pos[r]].front();
		s[++top]=x;
		tot[pos[r]][x]--,q[pos[r]].pop_front();
	}
	top--;
	while(top){
		int x=s[top];
		q[pos[r]].push_front(x);
		tot[pos[r]][x]++;
		top--;
	}
}
int query(int l,int r,int k){
	int ans=0;
	if(pos[l]==pos[r]){
		int now=pos[l];
		l-=st[now],r-=st[now];
		for(int i=l;i<=r;i++){
			if(q[now][i]==k) ans++;
		}
		return ans;
	}
	int cnt1=ed[pos[l]]-l+1,cnt2=r-st[pos[r]]+1;
	//处理左碎块 
	for(int i=q[pos[l]].size()-cnt1;i<=q[pos[l]].size()-1;i++){
		if(q[pos[l]][i]==k) ans++;
	} 
//	//处理右碎块
	for(int i=0;i<=cnt2-1;i++){
		if(q[pos[r]][i]==k) ans++;
	} 
//	cout<<"Aapwp"<<ans<<endl;
	for(int i=pos[l]+1;i<=pos[r]-1;i++){
		ans+=tot[i][k];
	}
//	cout<<"Aapwp"<<ans<<endl;
	return ans;
}

int main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	init();
	cin>>m;
	int lans=0;
	while(m--){
		cin>>op>>l>>r;
		l=((lans+l-1)%n)+1;
		r=((lans+r-1)%n)+1;
		if(l>r) swap(l,r);
		if(op==1){
			modify(l,r);	
		}
		else{
			cin>>k;
			k=((lans+k-1)%n)+1;
			lans=query(l,r,k);
			cout<<lans<<endl;
		}
	}
}
posted @ 2024-07-31 20:46  Aapwp  阅读(14)  评论(0编辑  收藏  举报
我给你一个没有信仰的人的忠诚