NOIP2016 普及组第四题 魔法阵magic 题解

题目描述

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

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

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

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

输入输出格式

输入格式:
输入文件的第一行包含两个空格隔开的正整数n和m。

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

保证每个Xi是分别在合法范围内等概率随机生成的。

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

保证标准输出中的每个数都不会超过10^9。

每行相邻的两个数之间用恰好一个空格隔开。

输入输出样例

输入样例#1:
30 8
1
24
7
28
5
29
26
24
输出样例#1:
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
输入样例#2:
15 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
输出样例#2:
5 0 0 0
4 0 0 0
3 5 0 0
2 4 0 0
1 3 0 0
0 2 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 0 1 0
0 0 2 1
0 0 3 2
0 0 4 3
0 0 5 4
0 0 0 5

题解

对于一道普及组的题目来说,这道题的难度还是有点大的
首先要读懂题,题目中有大写X和小写x,(在上面我已经改了),其实是一样的,可能会有人因此混淆

列出条件
1. xa<xb<xc<xd
2. xbxa=2(xdxc)
3. xbxa<(xcxb)/3


先考虑稍微暴力点的方法
对于第一个条件,直接将所有东西存进一个桶里,桶的大小不会超过N(15000),每次不是枚举第几个魔法值,而是枚举魔法值
第二个条件的意思是后前两个数的差等于后两个数的差的两倍
可以利用条件三加一个小优化,这样在统计答案的时候也就不用判断是否满足条件三了
设后两个数的差为 j
xbxa<(xcxb)/3
2j<(xcxb)/3
6j<xcxb
xc>xb+6j
这样,在枚举xc的时候就从 6j+xb+1 开始枚举就行了
那么就可以考虑枚举后两个数的差,可以算出前两个数的差,再枚举xa算出xb,枚举xc算出xd然后统计答案
如何统计答案?
详细来说,设t[x]表示x这个值有多少个,设 ans[x][1 4] 表示x这个数作为第一个道第四个数出现了几次,那么
ans[xa][1]=t[xb]t[xc]t[xd]
ans[xb][2]=t[xa]t[xc]t[xd]
ans[xc][3]=t[xa]t[xb]t[xd]
ans[xd][4]=t[xb]t[xc]t[xd]
输出时对于每个输入的魔法值x,输出 ans[x][1],ans[x][2],ans[x][3],ans[x][4]
预计得分:60~85


考虑优化
同样先枚举后两个数的差 j
那么对于xa=0的时候,另外三个数的最小值为:
xb=2j
xc=8j+1
xc=9j+1
是根据三个条件得出的
每次只需枚举xa,算答案的时候对于前两个数直接加上后两个数所有的可能情况之和,对于后两个数直接加上前两个数所有可能的情况之和

如何求呢?

用sum1记录从最小的xa(值为1)到当前的xa和所对应的xb的 t[xa]t[xb]
sum1=t[xa]t[xb]
类似,用sum2记录xd从当前xa可能的最小值(xd=xa+9*j+1)到最大xd(xd=n)和所对应xc的 t[xc]t[xd]
sum2=t[xc]t[xd]
那么
ans[xa][1]=t[xb]sum2
ans[xb][2]=t[xa]sum2
ans[xc][3]=t[xd]sum1
ans[xd][4]=t[xc]sum1
不理解的话可以在我的代码中看一看

代码

#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 41000
using namespace std;
int n,m,e[N],t[N],ans[N][4];
void read(int &x)
{
    char c;c=getchar();int n=0;
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar()) n=n*10+c-48;
    x=n;
}
int main()
{
    freopen("magic.in","r",stdin);freopen("magic.out","w",stdout);
    read(n);read(m);
    fo(i,1,m) read(e[i]),t[e[i]]++;
    int b,c,d;
    fo(j,1,2000)
    {
        b=2*j;c=8*j+1;d=c+j;
        int sum=0,sum2=0;
        fo(i,d+1,n) sum+=t[i]*t[i-j];
        fo(a,1,n)
        {
            b++;c++;d++;
            sum2+=t[a]*t[b];
            if(d>n) break;
            if(t[a]*t[b]>0)
            {
                ans[a][0]+=t[b]*sum;
                ans[b][1]+=t[a]*sum;
            }
            if(t[c]*t[d]>0)
            {
                ans[c][2]+=t[d]*sum2;
                ans[d][3]+=t[c]*sum2;
            }
            sum-=t[d]*t[c];
        }
    }
    fo(i,1,m) printf("%d %d %d %d\n",ans[e[i]][0],ans[e[i]][1],ans[e[i]][2],ans[e[i]][3]);
}

你可能感兴趣的:(题解,数学题,暴力)