概述
F a r m e r J o h n S o l v e s 3 S U M Farmer John Solves 3SUM Farmer John Solves 3SUM
题目描述
F a r m e r J o h n Farmer John Farmer John 相信他在算法设计上实现了一个重大突破:他声称他发现了一个 3 S U M 3SUM 3SUM 问题的近似线性时间算法,这是一个有名的算法问题,尚未发现比运行速度比平方时间明显更优的解法。 3 S U M 3SUM 3SUM 问题的一个形式是:给定一个整数数组 s 1 , … , s m s_1,ldots,s_m s1,…,sm,计算不同索引组成的无序三元对 i , j , k i,j,k i,j,k 的数量,使得 s i + s j + s k = 0 s_i + s_j + s_k = 0 si+sj+sk=0
为了测试 F a r m e r J o h n Farmer John Farmer John 的断言, B e s s i e Bessie Bessie 提供了一个 N N N 个整数组成的数组 A A A( 1 ≤ N ≤ 5000 1 leq N leq 5000 1≤N≤5000)。 B e s s i e Bessie Bessie 还会进行 Q Q Q 次询问( 1 ≤ Q ≤ 1 0 5 1 leq Q leq 10^5 1≤Q≤105),每个询问由两个索引 1 ≤ a i ≤ b i ≤ N 1 leq a_i leq b_i leq N 1≤ai≤bi≤N 组成。对于每个询问, F a r m e r J o h n Farmer John Farmer John 必须在子数组 A [ a i … b i ] A[a_i ldots b_i] A[ai…bi] 上求解 3 S U M 3SUM 3SUM 问题。
不幸的是, F a r m e r J o h n Farmer John Farmer John 刚刚发现了他的算法中的一个错误。他很自信他能修复这个算法,但同时,他请你帮他先通过 B e s s i e Bessie Bessie 的测试!
输入格式
输入的第一行包含两个空格分隔的整数 N N N 和 Q Q Q。
第二行包含空格分隔的数组 A A A 的元素 A 1 , … , A N A_1,ldots ,A_N A1,…,AN 。
以下 Q Q Q 行每行包含两个空格分隔的整数 a i a_i ai 和 b i b_i bi ,表示一个询问。
保证对于每个数组元素 A i A_i Ai 有 − 1 0 6 ≤ A i ≤ 1 0 6 -10^6 leq A_i leq 10^6 −106≤Ai≤106 。
输出格式
输出包含 Q Q Q 行,第 i i i 行包含一个整数,为第 i i i 个询问的结果。注意你需要使用 64 64 64 位整数型来避免溢出。
输入输出样例
输入 #1
7 3
2 0 -1 1 -2 3 3
1 5
2 4
1 7
输出 #1
2
1
4
说明/提示
样例解释
对于第一个询问,所有的三元对为 ( A 1 , A 2 , A 5 ) (A_1,A_2,A_5) (A1,A2,A5) 和 ( A 2 , A 3 , A 4 ) (A_2,A_3,A_4) (A2,A3,A4) 。
题目大意
有一个长度为 N N N 的序列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an 。
Q Q Q 次询问,每次给定 l l l 和 r r r ,问有多少三元组 ( i , j , k ) (i,j,k) (i,j,k) 满足 l ≤ i < j < k ≤ r lleq i < j < k leq r l≤i<j<k≤r 且 a i + a j + a k = 0 a_i + a_j + a_k = 0 ai+aj+ak=0 。
数据范围 : 1 ≤ N ≤ 5000 , − 1 0 6 ≤ a i ≤ 1 0 6 , 1 ≤ Q ≤ 1 0 5 1 leq N leq 5000 , -10^6 leq a_i leq 10^6 , 1 leq Q leq 10^5 1≤N≤5000,−106≤ai≤106,1≤Q≤105 。
解题思路
首先对于所有可能的 i , k i,k i,k ( 1 ≤ i < k ≤ N 1 leq i < k leq N 1≤i<k≤N) , 计算有多少 j j j 满足 i < j < k i < j < k i<j<k 且 a j = − a i − a k a_j=-a_i-a_k aj=−ai−ak,记作 f i , k f_{i,k} fi,k 。
查找的过程可以用桶来实现,但要记得每次找完一个区间 [ i , k ] [i,k] [i,k] 内的 j j j 时,记得把桶清空。
需要注意的是 a i a_i ai 可能为负数,所以对每一个要在桶操作的数,都得加上一个足够大的数,保证数组的下标不能是负数。
对于一组询问 l , r l,r l,r ,答案即为 ∑ i = l r ∑ k = i r f i , k sumlimits_{i=l}^r sumlimits_{k=i}^r f_{i,k} i=l∑rk=i∑rfi,k , 可用二维前缀、后缀和计算。
时间复杂度:
- 预处理 : O ( N 2 ) O(N^2) O(N2)
- 查询 : O ( 1 ) O(1) O(1)
A C c o d e AC code AC code
阅读时请省略快读 —— QAQ
#include <bits/stdc++.h>
#define int long long
#define _ 1000000
using namespace std;
/* --------------- fast io --------------- */ // begin
namespace Fread
{
const int SIZE = 1 << 21;
char buf[SIZE], *S, *T;
inline char getchar()
{
if (S == T)
{
T = (S = buf) + fread(buf, 1, SIZE, stdin);
if (S == T)
return 'n';
}
return *S++;
}
} // namespace Fread
namespace Fwrite
{
const int SIZE = 1 << 21;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush()
{
fwrite(buf, 1, S - buf, stdout);
S = buf;
}
inline void putchar(char c)
{
*S++ = c;
if (S == T)
flush();
}
struct NTR
{
~NTR() { flush(); }
} ztr;
} // namespace Fwrite
#ifdef ONLINE_JUDGE
#define getchar Fread ::getchar
#define putchar Fwrite ::putchar
#endif
namespace Fastio
{
struct Reader
{
template <typename T>
Reader &operator>>(T &x)
{
char c = getchar();
T f = 1;
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
x = 0;
while (c >= '0' && c <= '9')
{
x = x * 10 + (c - '0');
c = getchar();
}
x *= f;
return *this;
}
Reader &operator>>(char &c)
{
c = getchar();
while (c == ' ' || c == 'n')
{
c = getchar();
}
return *this;
}
Reader &operator>>(char *str)
{
int len = 0;
char c = getchar();
while (c == ' ' || c == 'n')
{
c = getchar();
}
while (c != ' ' && c != 'n' && c != 'r')
{ // rn in windows
str[len++] = c;
c = getchar();
}
str[len] = '