一道回滚莫队的题=。=

3.1 Description
然而贪玩的dirty 又开始了他的第三个游戏。
dirty 抓来了n 只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从1 到n。最开始,它们按某个
顺序排成一列。现在dirty 想要进行m 场比赛,每场比赛给出l 和r ,表示选出从左向右数第l
只至第r 只蚂蚁。被选出的蚂蚁需要快速地按编号从小到大排序,之后这些蚂蚁中编号连续的蚂
蚁将围成一个圈。每场比赛结束后,蚂蚁们还需要快速地回到最开始的位置。
按照蚂蚁的审美标准,围成的圈越大美观值就越大。于是dirty 每次需要找到最大的圈,但由
于比赛多到难以处理,他只需要知道这个圈中蚂蚁的数目。
3.2 Input
第一行为两个整数n,m,分别表示蚂蚁的总数和比赛场数。
接下来一行n 个数,表示从左向右依次的蚂蚁编号。
再接下来m 行,每行两个数l、r,表示将从左向右数第l 只至第r 只蚂蚁选出进行比赛。
3.3 Output
输出m 行,每行一个整数表示该次询问的答案。
3.4 Sample Input
8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7
3.5 Sample Output
3
3
4
3.6 Note
对于20% 的数据,n 500;m 500;
对于50% 的数据,n 30000;m 30000;
对于100% 的数据,n 100000;m 100000。
 

普通莫队过不了这道题,这时有了回滚莫队(只能离线处理一段区间啊啊!!!)

把1到n分成√n 份。对于所有询问按左端点先从小到大排序;

一块一块的处理;

对于每一块,先找出所有左端点在块中的询问,然后再按右端点从小到大排序,

左指针最开始指向这块的最末一个元素,

右指针指向下一块的第一个元素。

一个一个询问来处理,右指针只会一直往右动(同块询问,按右端点从小到大排序),所以只会加入不会删减。

对于每一个询问,左指针一点一点向左往当前询问左端点动。用一个栈来记录加入了哪些点。

动到前询问左端点时,完成本次询问,滚回到这块的最末一个元素。(即弹栈)。

当这块所有询问处理完后,处理下一块=。=

#include<bits/stdc++.h>
using namespace std;
int p[100005], ll[100005], rr[100005],ans[100005];
struct node
{
	int l, r, num; 
};
node q[100005], qx[100005];
int zu, zhan[100005], vis[100005], top, rans, tag[100005], n, m;
bool cmp(const node a,const node b)
{
	if(a.l == b.l) return a.r < b.r;
	return a.l < b.l;
}
bool cmp1(const node a,const node b)
{
	if(a.r == b.r) return a.l < b.l;
	return a.r < b.r;
}
void update(int x)
{
	if(tag[x] != zu) {
		tag[x] = zu; ll[x] = x; rr[x] = x; vis[x] = 0; 
	}
}
void modify(int x, int flag)
{
	update(x); update(x+1); update(x-1);
	int l = x,r = x;
	if(x != 1 && vis[x-1] == 1) l = ll[x-1];
	if(x != n && vis[x+1] == 1) r = rr[x+1];
	ll[r] = l ; rr[l] = r; vis[x] = 1;
	rans = max(rans, r - l +1);
	if(flag == 1) zhan[++top] = x;
}
void restore()
{
	while(top)
	{
		int x = zhan[top];
		top--;
		if(x != 1 && vis[x-1] == 1) rr[ll[x-1]] = x - 1;
        if(x != n && vis[x+1] == 1) ll[rr[x+1]] = x + 1;
		    vis[x] = 0;   		 
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	scanf("%d",&p[i]);
	for(int i = 1;  i <= m; i++)
	{
		scanf("%d%d",&qx[i].l,&qx[i].r);
		qx[i].num = i;
	}
	int  now = 1;
	sort(qx+1,qx+1+m,cmp);//把所有询问按左端点排序
	int kuai = (int)(sqrt(n) + 1e-8);
	for(int i = 1; i <= n; i += kuai)
	{
		int shu = 0;rans = 1;
		while(shu <= m&&qx[now].l >= i && qx[now].l <= min(n,(i + kuai - 1))){q[++shu] = qx[now];now++;}//找出左端点在这一块的所有询问
		if(shu == 0) continue;
		zu ++;
		sort(q+1,q+1+shu,cmp1);//同一块的询问按右端点排序
		int nowr = min(n,i + kuai - 1);
		for(int j = 1; j <= shu; j++)
		{
			while(nowr < q[j].r)
			{
				int t = p[++nowr];
				modify(t,0);
			}//右指针向右动。
			int temp = rans;//右指针所做的贡献对下一个询问有用,所以单独保存
			for(int k = min(q[j].r,min(n,i + kuai - 1)); k >= q[j].l; k--)
			{
				int t = p[k];
				modify(t,1);
			}//左指针向左动。
			ans[q[j].num] = rans;
			restore();//左指针往回滚
			rans = temp;
		}
	}
	for(int i = 1; i <= m;i++)
	{
		cout << ans[i] << endl;
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Bluelanzhan/article/details/83063972