【题解】魔法阵

题目描述

  六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。

  大魔法师有m个魔法物品,编号分别为1,2,...,m。每个物品具有一个魔法值,我们用xi表示编号为i的物品的魔法值。每个魔法值xi是不超过n的正整数,可能有多个物品的魔法值相同。

  大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足xa<xb<xc<xd,xb−xa=2(xd−xc),并且xb−xa<(xc−xb)÷3时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的A物品,B物品,C物品,D物品。

  现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的A物品出现的次数,作为B物品的次数,作为C物品的次数,和作为D物品的次数。

 

输入格式

  第一行包含两个空格隔开的正整数n和m。

  接下来m行,每行一个正整数,第i+1行的正整数表示xi,即编号为i的物品的魔法值。

  保证1≤n≤ 15000,1≤m≤40000,1≤xi≤n。每个xi是分别在合法范围内等概率随机生成的。

 

输出格式

  输出m行,每行四个整数。第i行的四个整数依次表示编号为i的物品作为A,B,C,D物品分别出现的次数。

  保证标准输出中的每个数都不会超过10^9。每行相邻的两个数之间用恰好一个空格隔开。

 

输入样例

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

 

题解

  朴素的方法自然是枚举$a$、$b$、$c$、$d$,但是四个未知数显然不科学。所以我们要消元,用一两个未知数来表示他们之间的关系。

  因为有$x_{b} - x_{a} = 2(x_{d} - x_{c})$,我们可以设$x_{d} - x_{c} = i$,则$x_{b} - x_{a} = 2i$。

  又因为有$3(x_{b} - x_{a}) < x_{c} - x_{b}$,即$6i < x_{c} - x_{b}$,所以我们又设$6i + k = x_{c} - x_{b}$。

  则此时我们已知方程组:

  $$\left \{ \begin{matrix} x_{a} + 2i = x_{b} \\ x_{b} + 6i + k = x_{c} \\ x_{c} + i = x_{d} \end{matrix} \right.$$

  所以,我们就只需要枚举$i$和$k$了。

  我们设$x_{i}$的个数为$f[x_{i}]$,设$t[x_{i}][A,B,C,D]$分别表示$x_{i}$作为$A,B,C,D$物品的次数。

  又设已知$a,b$时,$c,d$的个数为$sum_{1}$;已知$c,d$时,$a,b$的个数为$sum_{2}$。

  易得:

  $$\begin{align*} t[x_{a}][A] &= f[x_{b}] \times sum_{1} \\ t[x_{b}][B] &= f[x_{a}] \times sum_{1} \\ t[x_{c}][C] &= f[x_{d}] \times sum_{2}  \\  t[x_{d}][D] &= f[x_{c}] \times sum_{2} \end{align*}$$

  我们在枚举$a,b(c,d)$的时候,也不断累加$sum_{1}(sum_{2})$即可。

#include <iostream>
#include <cstdio>

#define MAX_N (15000 + 5)
#define MAX_M (50000 + 5)

using namespace std;

int n, m;
int x[MAX_M];
int f[MAX_N];
long long t[MAX_N][4];

int main()
{
    scanf("%d%d", &n, &m);
    for(register int i = 1; i <= m; ++i)
    {
        scanf("%d", x + i);    
        ++f[x[i]];
    }
    int a, b, c, d;
    long long sum;
    for(register int i = 1; 9 * i + 1 < n; ++i)
    {
        a = n - 9 * i - 1;
        b = n - 7 * i - 1;
        c = n - i;
        d = n;
        sum = 0;
        while(a)
        {
            sum += f[c] * f[d];
            t[a][0] += f[b] * sum;
            t[b][1] += f[a] * sum;
            --a;
            --b;
            --c;
            --d;
        }
        a = 1;
        b = 2 * i + 1;
        c = 8 * i + 2;
        d = 9 * i + 2;
        sum = 0;
        while(d <= n)
        {
            sum += f[a] * f[b];
            t[c][2] += f[d] * sum;
            t[d][3] += f[c] * sum;
            ++a;
            ++b;
            ++c;
            ++d;
        }
    }
    for(register int i = 1; i <= m; ++i)
    {
        printf("%lld %lld %lld %lld\n", t[x[i]][0], t[x[i]][1], t[x[i]][2], t[x[i]][3]);
    }
    return 0;
}
参考程序

 

转载于:https://www.cnblogs.com/kcn999/p/11200846.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值