您的位置:首页 > 其它

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

2016-11-28 19:45 281 查看

题目描述

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

大魔法师有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的物品的魔法值。

保证每个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.xb−xa=2(xd−xc)

3.xb−xa<(xc−xb)/3

先考虑稍微暴力点的方法

对于第一个条件,直接将所有东西存进一个桶里,桶的大小不会超过N(15000),每次不是枚举第几个魔法值,而是枚举魔法值

第二个条件的意思是后前两个数的差等于后两个数的差的两倍

可以利用条件三加一个小优化,这样在统计答案的时候也就不用判断是否满足条件三了

设后两个数的差为j

xb−xa<(xc−xb)/3

2∗j<(xc−xb)/3

6∗j<xc−xb

xc>xb+6∗j

这样,在枚举xc的时候就从6∗j+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=2∗j

xc=8∗j+1

xc=9∗j+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<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 41000
using namespace std;
int n,m,e
,t
,ans
[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]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: