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
说明/提示
\(N\leq 100000,M\leq 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/