P4396 [AHOI2013]作业 (莫队+值域分块)

P4396 [AHOI2013]作业

题目描述

此时己是凌晨两点,刚刚做了 (Codeforces) 的小 A 掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可以做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文……小 A 压力巨大。

这时小 A 碰见了一道非常恶心的数学题,给定了一个长度为 (n) 的数列和若干个询问,每个询问是关于数列的区间表示数列的第 (l) 个数到第 (r) 个数),首先你要统计该区间内大于等于 (a),小于等于 (b) 的数的个数,其次是所有大于等于 (a),小于等于 (b) 的,且在该区间中出现过的数值的个数。

小 A 望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。

输入格式

第一行两个整数 (n,m)

接下来 (n) 个不超过 (10^5) 的正整数表示数列

接下来 (m) 行,每行四个整数 (l,r,a,b),具体含义参见题意。

输出格式

输出 (m) 行,分别对应每个询问,输出两个数,分别为在 (l)(r) 这段区间中大小在 ([a,b]) 中的数的个数,以及大于等于 (a),小于等于 (b) 的,且在该区间中出现过的数值的个数(具体可以参考样例)。

输入输出样例

输入 #1

3 4
1 2 2
1 2 1 3
1 2 1 1
1 3 1 3
2 3 2 3

输出 #1

2 2
1 1
3 2
2 1

说明/提示

(Nleq 100000,Mleq 100000,读入的数字均为 [1,10^5]) 内的正整数。

Solution

心心念念的 值域分块(+)莫队 来了

可以发现如果没有取值在([a,b])这个限制,这道题就是 P1972 [SDOI2009]HH的项链,那么就是莫队的裸题了

那么现在虽然有了这个范围,我们依然可以用莫队的思想,先计算出出([l,r])区间内的不同值的个数和种类(裸的莫队板子),再去统计满足([a,b])值域的答案

不会莫队可以去看看博主的这篇博客(嘻~)

如果当前加入这个值之后,(cnt[])数组为1,说明这个值是第一次加入,我们把种类数(+1)
同样的,如果删除这个数之后,(cnt[])数组变为了0,说明当前区间内已经没有这个值了,我们把种类数(-1)


void add(int x) {
	if((++cnt[a[x]])==1) kind[belong[a[x]]]++;
	sum[belong[a[x]]]++;
}

void remove(int x) {
	if(!(--cnt[a[x]])) kind[belong[a[x]]]--;
	sum[belong[a[x]]]--;
}

怎么统计呢? 那么值域分块就来了

一般的分块是对序列操作,是一种优美的暴力,可以在(O(sqrt n))的时间复杂度内统计出答案.再回过头来看一看,本次的问题是不是非常的类似,因此我们就可以对值域进行分块

将询问中出现的最大值设为(maxn),我们将([0,maxn])分为(sqrt {maxn})个块
在询问中,我们把([a,b])分成([a,belong[a]*block]),([belong[a]+1,belong[b]-1]),([(belong[b]-1)*block+1,b])进行统计,和常规分块询问操作一样

这一段代码里的([l,r])就是值域([a,b]),(tot)代表总数,(cal)代表种类,大块直接统计,小块(不完整的块)暴力统计,最后返回答案(因为懒得写两个函数所以开了个结构体)


Q query(int l,int r) {
	int tot=0,cal=0;
	for (int i=l;i<=min(belong[l]*block,r);i++) {
		if(cnt[i]) cal++;
		tot+=cnt[i];
	}
	if(belong[l]!=belong[r]) {
		for (int i=(belong[r]-1)*block+1;i<=r;i++) {
			if(cnt[i]) cal++;
			tot+=cnt[i];
		}
	}
	for (int i=belong[l]+1;i<=belong[r]-1;i++) {
		tot+=sum[i];
		cal+=kind[i];
	}
	return Q{tot,cal};
}

下面是总代码
其实如果学过分块和莫队的话,应该还是比较好理解的

Code

#include<bits/stdc++.h>
#define lol long long
#define mid (l+r>>1)
#define ll(i) (i<<1)
#define rr(i) (i<<1|1)
#define in(i) (i=read())
using namespace std;

const int N=1e5+10,mod=1e9+7;

int read() {
    int ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48),i=getchar();
    return ans*f;
}

int n,m,block,maxn,a[N];
int cnt[N],belong[N];
int sum[N],kind[N];
int ans1[N],ans2[N];

struct query{
	int l,r,qa,qb,id,pos;
	bool operator < (const query &a) const {
		return pos==a.pos?r<a.r:pos<a.pos;
	}
}t[N];

struct Q {int sum,cnt;};

void add(int x) {
	if((++cnt[a[x]])==1) kind[belong[a[x]]]++;
	sum[belong[a[x]]]++;
}

void remove(int x) {
	if(!(--cnt[a[x]])) kind[belong[a[x]]]--;
	sum[belong[a[x]]]--;
}

Q query(int l,int r) {
	int tot=0,cal=0;
	for (int i=l;i<=min(belong[l]*block,r);i++) {
		if(cnt[i]) cal++;
		tot+=cnt[i];
	}
	if(belong[l]!=belong[r]) {
		for (int i=(belong[r]-1)*block+1;i<=r;i++) {
			if(cnt[i]) cal++;
			tot+=cnt[i];
		}
	}
	for (int i=belong[l]+1;i<=belong[r]-1;i++) {
		tot+=sum[i];
		cal+=kind[i];
	}
	return Q{tot,cal};
}

int main() {
	in(n), in(m), block=sqrt(n);
	for (int i=1;i<=n;i++) in(a[i]);
	for (int i=1;i<=m;i++) {
		in(t[i].l), in(t[i].r);
		in(t[i].qa), in(t[i].qb);
		t[i].id=i, t[i].pos=(t[i].l-1)/block+1;
		maxn=max(maxn,t[i].qb);
	}
	sort(t+1,t+1+m);
	block=sqrt(maxn);
	for (int i=1;i<=maxn;i++) belong[i]=(i-1)/block+1;
	for (int i=1,curl=1,curr=0;i<=m;i++) {
		int l=t[i].l,r=t[i].r,qa=t[i].qa,qb=t[i].qb;
		while(curl<l) remove(curl++);
		while(curl>l) add(--curl);
		while(curr<r) add(++curr);
		while(curr>r) remove(curr--);
		Q ans=query(qa,qb);
		ans1[t[i].id]=ans.sum;
		ans2[t[i].id]=ans.cnt;
	}
	for (int i=1;i<=m;i++) cout<<ans1[i]<<" "<<ans2[i]<<endl;
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
原文地址:https://www.cnblogs.com/real-l/p/15136800.html