题目描述
六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。
大魔法师有m个魔法物品,编号分别为1,2,…,m。
每个物品具有一个魔法值,我们用 x i x_i xi表示编号为i的物品的魔法值。
每个魔法值 x i x_i xi是不超过n的正整数,可能有多个物品的魔法值相同。
大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足 x a < x b < x c < x d x_a<x_b<x_c<x_d xa<xb<xc<xd, x b − x a = 2 ( x d − x c ) x_b−x_a=2(x_d−x_c) xb−xa=2(xd−xc),并且 x b − x a < ( x c − x b ) / 3 x_b−x_a<(x_c−x_b)/3 xb−xa<(xc−xb)/3 时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的A物品,B物品,C物品,D物品。
现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的A物品出现的次数,作为B物品的次数,作为C物品的次数,和作为D物品的次数。
输入格式
输入文件的第一行包含两个空格隔开的正整数n和m。
接下来m行,每行一个正整数,第i+1行的正整数表示 x i x_i xi,即编号为i的物品的魔法值。
保证每个 x i x_i xi是分别在合法范围内等概率随机生成的。
输出格式
共输出m行,每行四个整数。
第i行的四个整数依次表示编号为i的物品作为A,B,C,D物品分别出现的次数。
保证标准输出中的每个数都不会超过109。
每行相邻的两个数之间用恰好一个空格隔开。
数据范围
1 ≤ n ≤ 15000 1≤n≤15000 1≤n≤15000,
1 ≤ m ≤ 40000 1≤m≤40000 1≤m≤40000,
1 ≤ x i ≤ n 1≤x_i≤n 1≤xi≤n
输入样例
30 8
1
24
7
28
5
29
26
24
输出样例
4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0
算法1
算法思想:暴力枚举(40分)
依次将每件物品作为魔法阵的A、B、C、D类物品进行枚举,选择符合以下条件的方案:
- x a < x b < x c < x d x_a<x_b<x_c<x_d xa<xb<xc<xd
- x b − x a = 2 × ( x d − x c ) x_b−x_a=2\times(x_d−x_c) xb−xa=2×(xd−xc)
- x b − x a < ( x c − x b ) / 3 x_b−x_a<(x_c−x_b)/3 xb−xa<(xc−xb)/3
时间复杂度
O ( m 4 ) O(m^4) O(m4)
C++ 代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 40010;
int x[N], sum[N][5];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i ++) scanf("%d", &x[i]);
for(int a = 1; a <= m; a ++)
for(int b = 1; b <= m; b ++)
for(int c = 1; c <= m; c ++)
for(int d = 1; d <= m; d ++)
//条件一:递增序列
if((x[a] < x[b]) && (x[b] < x[c]) && (x[c] < x[d]))
{
//条件二:xb−xa=2(xd−xc)
if((x[b] - x[a]) != 2 * (x[d] - x[c])) continue;
//条件三:xb−xa<(xc−xb)/3
//处理不能整除的情况
int t = (x[c] - x[b]) / 3;
if((x[c] - x[b]) % 3) t ++;
if((x[b] - x[a]) < t)
{
sum[a][1] ++, sum[b][2] ++, sum[c][3] ++, sum[d][4] ++;
}
}
for(int i = 1; i <= m; i ++)
{
printf("%d %d %d %d\n", sum[i][1], sum[i][2], sum[i][3], sum[i][4]);
}
return 0;
}
算法2
算法思想:桶排序,前缀和,数学,组合计数,乘法原理,加法原理(100分)
分析物品满足魔法阵的三个条件:
- x a < x b < x c < x d x_a<x_b<x_c<x_d xa<xb<xc<xd
- x b − x a = 2 × ( x d − x c ) x_b−x_a=2\times(x_d−x_c) xb−xa=2×(xd−xc)
- x b − x a < ( x c − x b ) / 3 x_b−x_a<(x_c−x_b)/3 xb−xa<(xc−xb)/3
为了减少枚举次数,必须找到A、B、C、D类物品魔法值之间的关系,这里 ( x d − x c ) (x_d−x_c) (xd−xc)最小,不妨设 t = ( x d − x c ) t = (x_d−x_c) t=(xd−xc),那么:
- x b − x a = 2 t x_b−x_a=2t xb−xa=2t
- 2 t < ( x c − x b ) / 3 2t<(x_c−x_b)/3 2t<(xc−xb)/3,整理得 x c − x b > 6 t x_c−x_b>6t xc−xb>6t;那么存在正整数 k k k,使得 x c − x b = 6 t + k x_c−x_b=6t+k xc−xb=6t+k。
那么A、B、C、D类物品魔法值在数轴上满足如下关系:
t t t的范围
根据题目描述,每个魔法值 x i x_i xi是不超过 n n n的正整数,那么A类物品的最小值为 1 1 1,D类物品的最大值为 n n n,线段AD的最大长度为 n − 1 n-1 n−1。而由图可知 A D = 2 t + 6 t + k + t AD=2t+6t+k+t AD=2t+6t+k+t, 9 t + k < = n − 1 9t+k<=n-1 9t+k<=n−1,而 k k k是正整数,最小值为1,所以 9 t < n − 1 9t<n-1 9t<n−1,记作 t < n − 1 9 t<\frac{n-1}{9} t<9n−1。即:
1 ≤ t < n − 1 9 1\le t < \frac{n-1}{9} 1≤t<9n−1
确定了t
的范围,就可以对红色部分和蓝色部分进行枚举,。
枚举D类物品的魔法值
通过题目描述, D D D的最大值为 n n n,从数轴上看最小值为 9 t + 2 9t+2 9t+2(即 A = 1 A=1 A=1且 k = 1 k=1 k=1),由此可以枚举 D D D类物品的魔法值,计算出 C C C的值:
C = D − t C=D-t C=D−t
因为使 A , B , C , D A,B,C,D A,B,C,D满足条件的 k k k的最小值为1,所以对于当前的 C C C和 D D D, A A A的最大值和 B B B的最大值分别为:
A = D − 9 t − 1 A=D-9t-1 A=D−9t−1 B = 2 t B=2t B=2t
由此根据乘法原理,通过cnt[x]
(表示魔法值为x
的物品的出现次数),计算在当前D
值的情况下,
- 魔法值为 C C C的物品作为魔法阵中编号为
c
的物品个数:
c = c n t [ A ] × c n t [ B ] × c n t [ D ] c=cnt[A]\times cnt[B]\times cnt[D] c=cnt[A]×cnt[B]×cnt[D] - 魔法值为 D D D的物品作为魔法阵中编号为
d
的物品个数:
d = c n t [ A ] × c n t [ B ] × c n t [ C ] d=cnt[A]\times cnt[B]\times cnt[C] d=cnt[A]×cnt[B]×cnt[C]
在其他条件不变的情况下,如果 A A A和 B B B比最大值小的话,只要 C C C和 B B B满足 X c − X b > 6 t X_c-X_b>6t Xc−Xb>6t,那么 A , B , C , D A,B,C,D A,B,C,D一定能组成魔法阵。即如果有 a 1 < a 2 , b 1 < b 2 a_1<a_2,b_1<b_2 a1<a2,b1<b2,且 a 2 , b 2 a_2,b_2 a2,b2能够和 C , D C,D C,D组成魔法阵,那么 a 1 , b 1 a_1,b_1 a1,b1也一定能够和 C , D C,D C,D组成魔法阵。因此可以使用前缀和的思想累加c
和d
的值,c[C]
和d[D]
的累加的次数是所有满足要求的 A A A和 B B B的 cnt[A] * cnt[B]
的和,再分别乘以cnt[D]
和cnt[C]
,即:
s u m + = c n t [ A ] × c n t [ B ] sum+=cnt[A] \times cnt[B] sum+=cnt[A]×cnt[B] c [ C ] + = s u m × c n t [ D ] c[C]+=sum \times cnt[D] c[C]+=sum×cnt[D] d [ D ] + = s u m × c n t [ C ] d[D]+=sum\times cnt[C] d[D]+=sum×cnt[C]
枚举A类物品的魔法值
同理可以枚举A类物品的魔法值。 A A A的最小值为1,最大值为 n − 9 t − 1 n-9t-1 n−9t−1(即 D = n D=n D=n且 k = 1 k=1 k=1)。此时:
B = A + 2 t B=A+2t B=A+2t
D D D和 C C C的最大值分别为:
D = A + 9 t + 1 D=A+9t+1 D=A+9t+1 C = D − t C=D-t C=D−t
根据乘法原理和前缀和思想:
s u m + = c n t [ C ] × c n t [ D ] sum+=cnt[C]\times cnt[D] sum+=cnt[C]×cnt[D] a [ A ] + = s u m × c n t [ B ] a[A]+=sum\times cnt[B] a[A]+=sum×cnt[B] b [ B ] + = s u m × c n t [ A ] b[B]+=sum\times cnt[A] b[B]+=sum×cnt[A]
时间复杂度
由于 A , B , C , D A, B, C, D A,B,C,D均在1到n之间,因此 1 ≤ t ≤ n − 2 9 1≤t≤\frac{n−2}{9} 1≤t≤9n−2。所以总枚举次数大约是: n − 2 9 × n = O ( n 2 9 ) = 2.5 × 1 0 7 \frac{n−2}{9}\times n=O(\frac{n^2}{9})=2.5×10^7 9n−2×n=O(9n2)=2.5×107。
参考文献
C++ 代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 15010, M = 40010;
//x[i]表示第i件物品的魔法值
//cnt[i]表示魔法值为i的物品的出现次数
//a[i]表示魔法值为i的作为魔法阵中a物品的次数
int x[M], cnt[N], a[N], b[N], c[N], d[N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i ++)
{
scanf("%d", &x[i]);
cnt[x[i]] ++;
}
//枚举t
for(int t = 1; 9 * t < n - 1; t ++)
{
int sum = 0;
//枚举右边区域中D的值
for(int D = 9 * t + 2; D <= n; D ++)
{
int C = D - t;
int A = D - 9 * t - 1; //满足条件的A的最大值
int B = A + 2 * t;
sum += cnt[A] * cnt[B]; //前缀和累加
c[C] += sum * cnt[D];
d[D] += sum * cnt[C];
}
sum = 0;
//枚举左边区域A的值
for(int A = n - 9 * t - 1; A >= 1; A --)
{
int B = A + 2 * t;
int D = A + 9 * t + 1; //满足条件的D的最大值
int C = D - t;
sum += cnt[C] * cnt[D]; //前缀和累加
a[A] += sum * cnt[B];
b[B] += sum * cnt[A];
}
}
for(int i = 1; i <= m; i ++)
{
printf("%d %d %d %d\n", a[x[i]], b[x[i]], c[x[i]], d[x[i]]);
}
return 0;
}