【Codeforces 455D】Serega and Fun | 分块、双端队列

题目链接:问题连接

题目大意:

给出一个长度为 N N N的序列,对这个序列进行下面两种操作:

  • 1 , l , r : 1 ,l,r: 1,l,r:将区间 [ l , r ] [l,r] [l,r]内循环移动一位
  • 2 , l , r , k : 2,l,r,k: 2,l,r,k:询问区间 [ l , r ] 内 [l,r]内 [l,r]有多少个数字 k k k,强制在线。

题目思路:

考虑分块就好了,将序列分成 s q r t ( n ) sqrt(n) sqrt(n)块,每一块维护一个双向队列。

对于一个区间的修改操作:在中间的块就可以直接通过 O 1 O1 O1的操作完成,两边的块特判一下就好了。

对于一个区间的询问操作:维护根号个块的数字种类数,完整的块直接用这个计算,不完整的直接特判。

复杂度: O ( m ∗ s q r t ( n ) ∗ 2 ) O(m*sqrt(n)*2) O(msqrt(n)2)

Code:

/*** keep hungry and calm CoolGuang!  ***/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e18+7;
const ll maxn = 1e5+700;
const ll mod= 100003;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
int c[350][maxn];
deque<int>q[350];
int L[maxn],R[maxn];
int b[maxn];
int num[maxn];
int block = 0;
int save[maxn];
void Modify(int l,int r){
	if(l>r) swap(l,r);
	int s = b[l],t = b[r];
	int cnt = 0;
	if(s == t){
		while(!q[s].empty()) save[++cnt] = q[s].back(),q[s].pop_back();
		l-= (s-1)*block,r-=(s-1)*block;
		int temp = save[r];
		for(int i=r;i>=l+1;i--) save[i] = save[i-1];
		save[l] = temp;
		for(int i=1;i<=cnt;i++) q[s].push_front(save[i]);
		return;
	}
	///t>s
	int last = q[s].front();
	for(int i=s+1;i<=t-1;i++){
		q[i].push_back(last);
		c[i][last]++;
		last = q[i].front();
		q[i].pop_front();
		c[i][last]--;
	}
	cnt = 0;
	///t块单独
	for(int i=(t-1)*block+1;i<=r;i++) c[t][q[t].back()]--,save[++cnt] = q[t].back(),q[t].pop_back();
	for(int i=cnt-1;i>=1;i--)c[t][save[i]]++,q[t].push_back(save[i]);
	q[t].push_back(last),c[t][last]++;
	last = save[cnt];
	cnt = 0;
	///s块单独
	for(int i=l;i<=R[s];i++) c[s][q[s].front()]--,save[++cnt] = q[s].front(),q[s].pop_front();
	q[s].push_front(last),c[s][last]++;
	for(int i=cnt;i>=2;i--) c[s][save[i]]++,q[s].push_front(save[i]);
}
ll Query(int k,int l,int r){
	if(l>r) swap(l,r);
	int s = b[l],t = b[r];
	int cnt = 0;
	int ans = 0;
	if(s == t){
		while(!q[s].empty()) save[++cnt] = q[s].back(),q[s].pop_back();
		l-= (s-1)*block,r-=(s-1)*block;
		for(int i=l;i<=r;i++) if(save[i] == k) ans++;
		for(int i=1;i<=cnt;i++) q[s].push_front(save[i]);
		return ans;
	}
	for(int i=s+1;i<=t-1;i++) ans += c[i][k];
	cnt = 0;
	///t块单独
	for(int i=(t-1)*block+1;i<=r;i++) ans += ((q[t].back() == k)?1:0),save[++cnt] = q[t].back(),q[t].pop_back();
	for(int i=cnt;i>=1;i--) q[t].push_back(save[i]);
	cnt = 0;
	///s块单独
	for(int i=l;i<=R[s];i++) ans += ((q[s].front() == k)?1:0),save[++cnt] = q[s].front(),q[s].pop_front();
	for(int i=cnt;i>=1;i--) q[s].push_front(save[i]);
	return ans;
}
int main(){

	/*q[1].push_front(2);
	q[1].push_front(3);
	q[1].push_back(1);
	debug(q[1].back());*/
	read(n);
	block = sqrt(n);
	for(int i=1;i<=n;i++) read(num[i]);
	for(int i=1;i<=n;i++){
		b[i] = (i-1)/block + 1;
		c[b[i]][num[i]]++;
		L[i] = (i%block == 1)?1:0;
		R[b[i]] = min(b[i]*block*1ll,n);
		q[b[i]].push_front(num[i]);
	}
	int lastans = 0;
	read(m);
	for(int i=1;i<=m;i++){
		int op,x,y,z;
		read(op);read(x);read(y);
		x = (x+lastans-1)%n + 1;
		y = (y+lastans-1)%n + 1;
		if(op == 1) Modify(x,y);
		else{
			read(z);
			z = (z+lastans-1)%n+1;
			lastans = Query(z,x,y);
			di(lastans);
		}
	}
    return 0;

}
/***
7
6 6 2 7 4 2 5
7
1 3 6
2 1 2 2
***/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只酷酷光儿( CoolGuang)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值