题目描述
六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。
大魔法师有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。
每行相邻的两个数之间用恰好一个空格隔开。
这道题目有许多做法,(但不是每种做法都能拿分),
在参观了一位大牛的代码后,我们都震惊了!(在此特别感谢学军的xxy大牛,虽然平生素未相见)
首先,我们来看题目。n<=15000,m<=40000。如果你只写四重循环的话,是用不到n的。但请记住,没有白给的条件,没有没用的数据。
大牛说:“我们可以把这m个点画在一个数轴上。”
如果ABCD是魔法阵的四个物品,那么根据题目,它们一定满足AB=2*CD,BC>6*CD,AD>9*CD。那么我们只需要确定D,就可以确定C点,然后再找AB。同理,我们也可以通过找C来确定ABD。
更多,详见代码:
/*
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
*/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<map>
using namespace std;
int a[15005],b[15005],c[15005],d[15005],w[15005],h[40005],n,m,x,y;
//abcd表示某个点作为abcd物品出现的次数;w表示数轴上每个点出现的次数;h表示每个物品的魔法值
//n表示最大魔法值,m表示物品数量
int main()
{
cin>>n>>m;
if(n<11)
{
for(int i=1;i<=m;i++) printf("0 0 0 0\n");
return 0;
}
for(int i=1;i<=m;i++)
{
scanf("%d",&h[i]);
w[h[i]]++;
}//把这些点标记在数轴上
for(int i=1;i<=n/9;i++)
{
//若数轴上有一个魔法阵:ABCD,其中有AB=2*CD,BC>6*CD
//所以只需枚举CD的长度就可以了
x=1+9*i,y=0;//x为AD最短长度
for(int j=2+9*i;j<=n;j++)
{
//因为数轴是从1开始的,所以从1+x开始枚举
//枚举D点即j,则C点为j-i,A点为j-x,B点为j-x+2*i
//CD的个数取决于AB有多少组,所以我们用y表示AB的组数
y+=w[j-x]*w[j-x+i+i];//y为AB的对数
//D点是不定的。但是D点变化时,之前合格的AB两点仍然合格,所以要累加
d[j]+=y*w[j-i];//有几组AB,就有几个C点,就有几个D点
c[j-i]+=y*w[j];//有几组AB,就有几个D点,就有几个C点
}
//注意,魔法值可能重复,所以在加的时候,注意不要直接加。
//同理,枚举CD两点,确定AB的个数
x=8*i+1,y=0;
for(int j=n-9*i-1;j>=1;j--)
{
y+=w[j+x]*w[j+x+i];
a[j]+=y*w[j+i+i];
b[j+i+i]+=y*w[j];
}
}
for(int i=1;i<=m;i++)//输出每个物品对应的魔法值的作为abcd物品的次数
cout<<a[h[i]]<<' '<<b[h[i]]<<' '<<c[h[i]]<<' '<<d[h[i]]<<endl;
}
/*
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
源自于XXY大犇的代码,在此特别感谢!
*/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<map>
using namespace std;
int a[15005],b[15005],c[15005],d[15005],w[15005],h[40005],n,m,x,y;
//abcd表示某个点作为abcd物品出现的次数;w表示数轴上每个点出现的次数;h表示每个物品的魔法值
//n表示最大魔法值,m表示物品数量
int main()
{
cin>>n>>m;
if(n<11)
{
for(int i=1;i<=m;i++) printf("0 0 0 0\n");
return 0;
}
for(int i=1;i<=m;i++)
{
scanf("%d",&h[i]);
w[h[i]]++;
}//把这些点标记在数轴上
for(int i=1;i<=n/9;i++)
{
//若数轴上有一个魔法阵:ABCD,其中有AB=2*CD,BC>6*CD
//所以只需枚举CD的长度就可以了
x=1+9*i,y=0;//x为AD最短长度
for(int j=2+9*i;j<=n;j++)
{
//因为数轴是从1开始的,所以从1+x开始枚举
//枚举D点即j,则C点为j-i,A点为j-x,B点为j-x+2*i
//CD的个数取决于AB有多少组,所以我们用y表示AB的组数
y+=w[j-x]*w[j-x+i+i];//y为AB的对数
//D点是不定的。但是D点变化时,之前合格的AB两点仍然合格,所以要累加
d[j]+=y*w[j-i];//有几组AB,就有几个C点,就有几个D点
c[j-i]+=y*w[j];//有几组AB,就有几个D点,就有几个C点
}
//注意,魔法值可能重复,所以在加的时候,注意不要直接加。
//同理,枚举CD两点,确定AB的个数
x=8*i+1,y=0;
for(int j=n-9*i-1;j>=1;j--)
{
y+=w[j+x]*w[j+x+i];
a[j]+=y*w[j+i+i];
b[j+i+i]+=y*w[j];
}
}
for(int i=1;i<=m;i++)//输出每个物品对应的魔法值的作为abcd物品的次数
cout<<a[h[i]]<<' '<<b[h[i]]<<' '<<c[h[i]]<<' '<<d[h[i]]<<endl;
}