【题解】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)\)

注意:

  1. 因为此题卡空间所以我们得直接用 \(f\) 数组来代替 \(s\) 数组。
  2. 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;
}
posted @ 2021-08-07 18:05  mango09  阅读(50)  评论(0编辑  收藏  举报
-->