P4867 Gty的二逼妹子序列

P4867 Gty的二逼妹子序列

题目描述

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题

对于一段妹子们,他们想让你帮忙求出这之内美丽度 (in [a,b])的妹子的美丽度的种类数。

为了方便,我们规定妹子们的美丽度全都在 ([1,n]) 中。
给定一个长度为(n(1 le n le 100000)) 的正整数序列(s(1 le si le n)),对于 (m(1 le m le 1000000))次询问l,r,a,b,每次输(s_l cdots s_r)​中,权值 (in [a,b]) 的权值的种类数。

输入格式

第一行包括两个整数 (n,m(1 le n le 100000,1 le m le 1000000)),表示数列 (s) 中的元素数和询问数。

第二行包括 (n) 个整数 (s1…sn(1 le si le n))

接下来 (m) 行,每行包括 (4) 个整数 (l,r,a,b(1 le l le r le n,1 le a le b le n)),意义见题目描述。

保证涉及的所有数在C++的int内。保证输入合法。

输出格式

对每个询问,单独输出一行,表示 (s_l cdots s_r) 权值 (in [a,b]) 的权值的种类数。

输入输出样例

输入 #1

10 10
4 4 5 1 4 1 5 1 2 1
5 9 1 2
3 4 7 9
4 4 2 5
2 3 4 7
5 10 4 4
3 9 1 1
1 4 5 9
8 9 3 3
2 2 1 6
8 9 1 4

输出 #1

2
0
0
2
1
1
1
0
1
2

说明/提示

【样例的部分解释】

5 9 1 2 子序列为4 1 5 1 2
在[1,2]里的权值有1,1,2,有2种,因此答案为2。

3 4 7 9
子序列为5 1 在[7,9]里的权值有5,有1种,因此答案为1。

4 4 2 5
子序列为1
没有权值在[2,5]中的,因此答案为0。

2 3 4 7
子序列为4 5
权值在[4,7]中的有4,5,因此答案为2。

建议使用输入/输出优化。


这题空间好鬼畜啊。没见过这么小气的出题人

首先这道题 询问可以离线,并且询问是关于颜色种类的,你想到了什么?

莫队 OR 分块?。

拿莫队写的话,统计答案比较麻烦,但处理询问的话比较方便。

但你拿分块写的话,虽然答案比较好处理,但却处理不了区间询问的问题。

那怎么办? 我们把这两个结合起来,搞个莫队套分块(具体来说是莫队套值域分块)。

那怎么操作呢?

我们在移动莫队的指针的时候,顺便维护一下每个权值出现的次数,并且维护一下每个权值所在块的颜色个数。

等到莫队指针与询问区间完全重合,就可以拿分块来统计答案。

实现的细节看代码吧;

注意块的 (L)(R) 这两个数组可以开小点,要卡着开。

答案数组不要开的太小了,(开小了的话会 TLE 又 MLE)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5+10;
int n,m,block;
int c[N],cnt[N],pos[N],sum[N],ans[1000010],L[1010],R[1010];
struct node
{
	int l,r,a,b,id;
}q[1000010];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
	return s * w;
}
bool comp(node a,node b)
{
	if(pos[a.l] == pos[b.l]) return a.r < b.r;
	return pos[a.l] < pos[b.l];
}
void add(int x)
{
	cnt[c[x]]++;//维护每个权值出现的次数
	if(cnt[c[x]] == 1) sum[pos[c[x]]]++;//第一次出现说明出现了一个新的种类
}
void del(int x)
{	
	if(cnt[c[x]] == 1) sum[pos[c[x]]]--;//出现次数变为0,说明出现的种类数减1
	cnt[c[x]]--;
}
int query(int x,int y)//分块询问区间和
{
	int res = 0;
	if(pos[x] == pos[y])
	{
		for(int i = x; i <= y; i++) if(cnt[i] >= 1) res++;
		return res;
	}
	else 
	{
//		cout<<"snkhg"<<endl;
		int xx = pos[x], yy = pos[y];
		for(int i = x; i <= R[xx]; i++) if(cnt[i] >= 1 ) res++;
		for(int i = xx+1; i <= yy-1; i++) res += sum[i];
		for(int i = L[yy]; i <= y; i++) if(cnt[i] >= 1) res++;
	}
	return res;
}
int main()
{
	n = read(); m = read(); block = (int) sqrt(n);
	for(int i = 1; i <= n; i++)
	{
		c[i] = read();
		pos[i] = (i-1) / block + 1;
		L[pos[i]] = 2333333;
	}
	for(int i = 1; i <= m; i++)
	{
		q[i].l = read(); q[i].r = read();
		q[i].a = read(); q[i].b = read();
		q[i].id = i;
	}
	for(int i = 1; i <= n; i++)
	{
		L[pos[i]] = min(L[pos[i]],i);
		R[pos[i]] = max(R[pos[i]],i);
	}
	sort(q+1,q+m+1,comp);
	int l = 1,r = 0;
	for(int i = 1; i <= m; i++)
	{
		while(l < q[i].l) del(l++);//莫队维护询问区间
		while(l > q[i].l) add(--l);
		while(r < q[i].r) add(++r);
		while(r > q[i].r) del(r--);
		ans[q[i].id] = query(q[i].a,q[i].b);//分块统计答案
	}
	for(int i = 1; i <= m; i++) printf("%d
",ans[i]);
//	fclose(stdin); fclose(stdout);
	return 0;
}


最近越来越喜欢刷数据结构的题了呢(大雾

原文地址:https://www.cnblogs.com/genshy/p/13639425.html