【题解】P6006
题意简述
给定一个整数数组 \(a_1,a_2,\cdots,a_n\),计算满足条件的无序三元对 \(i,j,k\) 的数量,使得 \(a_i+a_j+a_k=0\)。
本题思路
首先要注意的是,由于 \(-10^6\le a_i\le10^6\),所以我们需要给 \(a\) 数组集体加上 \(10^6\) 使得所有数均非负。
记 \(f_{i,j}\) 表示满足 \(k\in[l-1,r-1]\) 使得 \(a_i+a_j+a_k=0\) 的数量,我们可以枚举 \(i\) 和 \(j\) 算出 \(f_{i,j}\)。
令 \(s_{l,r}=\sum\limits_{i=1}^l\sum\limits_{j=1}^r f_{i,j}\),将 \(f_{i,j}\) 转化成平面上位置为 \((i,j)\) 的点,则 \(s_{l,r}\) 就是左上角为 \((1,1)\),右下角为 \((l,r)\) 的长方形内所有 \(f_{i,j}\) 的和。不难想到二维前缀和。
最后查询部分相当于 \(\sum\limits_{i=l}^r\sum\limits_{j=l}^r f_{i,j}\),即平面上左上角为 \((l,l)\),右下角为 \((r,r)\) 的长方形中所有 \(f_{i,j}\) 的和,可通过容斥原理计算。
时间复杂度:
处理处 \(f\) 数组和 \(s\) 数组时间复杂度为 \(O(n^2)\),每次询问为 \(O(1)\)。
注意:
- 因为此题卡空间所以我们得直接用 \(f\) 数组来代替 \(s\) 数组。
- 开
long long
!!!
本题代码
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int MAXN = 5005;
const int X = 1e6;
int a[MAXN], cnt[(X << 1) + 5], f[MAXN][MAXN];
signed main()
{
int n, q;
scanf("%lld%lld", &n, &q);
for (int i = 1; i <= n; i++)
{
scanf("%lld", a + i);
a[i] += X;
}
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
if (j > i + 1 && a[i] + a[j] <= 3 * X && a[i] + a[j] >= X) //k在i和j中间且a[k]不会超出1e6
{
f[i][j] = cnt[3 * X - a[i] - a[j]];
}
cnt[a[j]]++; //用桶来统计
}
for (int j = i + 1; j <= n; j++)
{
cnt[a[j]]--; //撤回选择
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
f[i][j] += f[i][j - 1] + f[i - 1][j] - f[i - 1][j - 1]; ///容斥原理计算
}
}
while (q--)
{
int l, r;
scanf("%lld%lld", &l, &r);
printf("%lld\n", f[r][r] - f[r][l - 1] - f[l - 1][r] + f[l - 1][l - 1]);
}
return 0;
}