Description
给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出
∑∞i=1get(l1,r1,x)get(l2,r2,x)
∑
i
=
1
∞
g
e
t
(
l
1
,
r
1
,
x
)
g
e
t
(
l
2
,
r
2
,
x
)
get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次。
Input
第一行,一个数字N,表示序列长度。
第二行,N个数字,表示a1~aN
第三行,一个数字Q,表示询问个数。
第4~Q+3行,每行四个数字l1,r1,l2,r2,表示询问。
N,Q≤50000
N1≤ai≤N
1≤l1≤r1≤N
1≤l2≤r2≤N
注意:答案有可能超过int的最大值
Output
对于每组询问,输出一行一个数字,表示答案
Sample Input
5
1 1 1 1 1
2
1 2 3 4
1 1 4 4
Sample Output
4
1
分析
我们要求的实际上就是区间[l1,r1]中的每个数在区间[l2,r2]中出现次数的和。
考虑按位置分块,预处理f[i,j]表示第i块中的每个数在[1,j]中出现次数的和。
询问的时候,整块的可以O(1)查询,那么多出来的怎么办呢?
如果拿可持久化线段树来查询的话,复杂度要多一个
logn−−−−√
l
o
g
n
。
注意到现在的瓶颈在于如何快速求[l2,r2]中某个数的出现次数,那么只要把询问按照[l2,r2]莫队一下就好了。
时间复杂度
O(nn−−√)
O
(
n
n
)
代码
#include <bits/stdc++.h>
const int N=50005;
const int M=250;
typedef long long ll;
int n,tot,bel[N],B,sta[M],end[M],t[N],a[N];
ll f[M][N],ans[N];
struct Data
{
int l1,r1,l2,r2,id;
}q[N];
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
void preWork()
{
B = sqrt(n);
for (int i = 1; i <= n; i++)
{
bel[i] = (i + B - 1) / B;
if (!sta[bel[i]])
sta[bel[i]] = i;
end[bel[i]] = i;
}
for (int i = 1; i <= bel[n]; i++)
{
for (int j = sta[i]; j <= end[i]; j++)
t[a[j]]++;
for (int j = 1; j <= n; j++)
f[i][j] = f[i][j - 1] + t[a[j]];
for (int j = sta[i]; j <= end[i]; j++)
t[a[j]] = 0;
}
}
bool cmp(Data a,Data b)
{
return bel[a.l2] < bel[b.l2] || bel[a.l2] == bel[b.l2] && a.r2 < b.r2;
}
ll query(int l1,int r1,int l2,int r2)
{
ll ans = 0;
if (bel[l1] == bel[r1])
{
for (int i = l1; i <= r1; i++)
ans += t[a[i]];
return ans;
}
for (int i = l1; i <= end[bel[l1]]; i++)
ans += t[a[i]];
for (int i = sta[bel[r1]]; i <= r1; i++)
ans += t[a[i]];
for (int i = bel[l1] + 1; i < bel[r1]; i++)
ans += f[i][r2] - f[i][l2 - 1];
return ans;
}
void solve()
{
for (int l = 1, r = 0, i = 1; i <= tot; i++)
{
for (; r < q[i].r2; r++)
t[a[r+1]]++;
for (; l > q[i].l2; l--)
t[a[l-1]]++;
for (; r > q[i].r2; r--)
t[a[r]]--;
for (; l < q[i].l2; l++)
t[a[l]]--;
ans[q[i].id] = query(q[i].l1,q[i].r1,q[i].l2,q[i].r2);
}
}
int main()
{
n = read();
for (int i = 1; i <= n; i++)
a[i] = read();
preWork();
tot = read();
for (int i = 1; i <= tot; i++)
q[i].l1 = read(), q[i].r1 = read(), q[i].l2 = read(), q[i].r2 = read(), q[i].id = i;
std::sort(q + 1, q + tot + 1, cmp);
solve();
for (int i = 1; i <= tot; i++)
printf("%d\n",ans[i]);
}