【原创】【NOIP2016普及组】魔法阵

题目描述

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

大魔法师有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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值