英才班2019-03测试

题目:

1、简单题
2、促销
3、黑洞
4、激光通讯
5、流星雨

1、简单题

【问题描述】

给定数轴上的N个点,求这些点到其他点的距离总和S。

【输入】

第一行一个整数N。
接下来N行每行一个整数,表示每个点的坐标x[i]。

【输出】

一个整数S。

【输入输出样例1】

样例输入:(cowdis.in)

5
1
5
3
2
4

样例输出:(cowdis.out)

40

【输入输出样例2】

样例输入:(cowdis.in)

8
10
20
30
40
50
60
70
80

样例输出:(cowdis.out)

1680

【数据范围】

对于30%的数据,1≤N≤10000
对于50%的数据,保证1≤x[i]。
对于70%的数据,保证1≤N≤300000,且任意两点的坐标不相同。
对于100%的数据,保证1≤N≤3000000,|x[i]|≤2^15-1。
【解析】
这道题真的很简单,只需求出递推式即可:
首先,我们从特殊情况看,假设只有a、b、c三点,那么距离之和:

sum=|a-b|+|a-c|+|b-a|+|b-c|+|c-a|+|c-b|=2*(|a-b|+|b-c|+|a-c|)

因此,我们推广到一般情况:
排过序的N个点为X1,X2,X3……,Xn,则对于

sum(X1)=2*[(X2-X1)+(X3-X1)+……+(Xn-X1)]=2*[ | ∑i=2n\sum_{i=2}^ni=2nXi - (n-1)*X1 | ]

同理,

sum(X2)=2*[ |∑i=3n\sum_{i=3}^ni=3n Xi - (n-2)*X2 | ]

……
于是,我们就可以得出递推式:

sum(Xm)=2*[ | ∑i=m+1n\sum_{i=m+1}^ni=m+1n Xi - (n-m)*Xm | ]

特别要注意的是:结果可能会爆int范围

【代码展示】

#include<bits/stdc++.h>
#define ud using namespace std
#define itn int
#define ll long long
#define fuck string
#define love char
#define fucker bool
#define re return
#define RP 12345678
ud;
int n;
ll x[30000300];
ll ans=0,sum=0;
int main()
{
	freopen("cowdis.in","r",stdin);
	freopen("cowdis.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%lld",&x[i]);
	sort(x+1,x+n+1);
	ll s=0;
	for(int i=1;i<=n;i++)
	s+=x[i];
	for(int i=1;i<=n;i++)
	{
		sum+=x[i];
		ans+=abs(s-sum-(n-i)*x[i]);
	}
	cout<<ans*2;
	return 0;
}

当然,其实还有另一种算法:

sum(Xi)=∑n=1i−1\sum_{n=1}^{i-1}n=1i1Xn - (i-1)*Xi

有兴趣的同学可以自主探究,下面给出核心代码:

for(int i=2;i<=n;i++)
{
	sum+=x[i-1];//前缀和
	ans+=abs(sum-(i-1)*x[i]);//递推式
}

2、促销

【问题描述】

小x在jzyz开了一家冷饮店,因为刚开张,他决定做一次促销活动。而活动的获奖者可以免费得到一杯圣代。
为了和同学的生活贴近,小x费劲脑汁想到了一个促销方案:
1) 当场摇出一个正整数M,再摇出两个正整数X和Y
2) 每个人可以在1…M这M个自然数中挑出N个不同的数。
3) 如果某人可以在现场把这N个数的倒数的累加和恰好等于X/Y,那么这个人可以得到一杯圣代。
4) 每种方案只限第一名想到答案的领取一杯圣代。
现在小x苦恼的是,对于给定的N,M,X,Y,他能否尽快得知他最多要准备多少杯圣代。

【输入】

四个整数:N,M,X,Y。

【输出】

一个整数,表示最多要准备多少杯圣代。

【输入输出样例1】

样例输入:(cd.in)

2 4 3 4

样例输出:(cd.out)

1

【数据范围】

对于30%的数据,1<=N<=M<=15
对于60%的数据,1<=N<=M<=20
对于100%的数据,1<=N<=M<=25,X<=25,Y<=25
【解析】
题目说了一大串,令人眼花缭乱,其实就是从1~M个自然数中选N个不同的数,使其倒数和等于X/Yの方案数。
显然,这里要用到双精度,然后我们可以进行深搜,那么该如何定义参数呢?
我们设置三个参数:
dfs(double sum,int n,int num)
其中sum表示倒数的累加和,n表示已经选了几个数,num表示当前搜到的第几个数字(优化)。如果不设置num,那么之前搜到的数字会在下一层里再搜一遍,很容易超时,所以设一个num进行优化,同时,我们再开一个f[i]数组标记第i个数是否被使用过。
对于边界,就很容易了:

if(n==N)
    {
		if(sum==X/Y)
		ans++;
		return;
	}

【代码展示】

#include<bits/stdc++.h>
#define ud using namespace std
#define itn int
#define ll long long
ud;
int n,m,ans=0;
double x,y;
double p;
bool f[50]={};
void dfs(double sum,int N,int Y)
{
	if(N==n)
	{
		if(sum==p)
		ans++;
		return;
	}
	for(int i=Y;i<=m;i++)
	{
		if(!f[i]&&sum+1.0/i<=p)
		{
			f[i]=1;
			dfs(sum+1.0/i,N+1,i);
			f[i]=0;
		}
	}
}
int main()
{
	freopen("cd.in","r",stdin);
	freopen("cd.out","w",stdout);
	scanf("%d%d",&n,&m);
	scanf("%lf%lf",&x,&y);
	p=x/y;
	for(int i=1;i<=m;i++)
	{
		if(1.0/i>p)
		continue;
		f[i]=1;
		dfs(1.0/i,1,i);
		f[i]=0;
	}
	printf("%d\n",ans);
	return 0;
}

然而,你会发现不能完美AC,只拿了90分,这是为什么呢?(O_o)??
之前说过,我们用的是双精度,它会有误差,因此我们需要一个常量L=0.00000005来加减比较一下,以后再碰到类似问题可千万不要忘!!!
【代码展示】

#include<bits/stdc++.h>
#define ud using namespace std
#define itn int
#define ll long long
ud;
int n,m,ans=0;
double x,y;
double p;
bool f[50]={};
void dfs(double sum,int N,int Y)
{
	if(N==n)
	{
		if(p+0.00000005>=sum&&p-0.00000005<=sum)
		ans++;
		return;
	}
	for(int i=Y;i<=m;i++)
	{
		if(!f[i]&&sum+1.0/i-0.00000005<=p)
		{
			f[i]=1;
			dfs(sum+1.0/i,N+1,i);
			f[i]=0;
		}
	}
}
int main()
{
	freopen("cd.in","r",stdin);
	freopen("cd.out","w",stdout);
	scanf("%d%d",&n,&m);
	scanf("%lf%lf",&x,&y);
	p=x/y;
	for(int i=1;i<=m;i++)
	{
		if(1.0/i-0.00000005>p)
		continue;
		f[i]=1;
		dfs(1.0/i,1,i);
		f[i]=0;
	}
	printf("%d\n",ans);
	return 0;
}

3、黑洞

【问题描述】

李宗泽的爱好是在周末进行物理学实验,但事与愿违,实验将N个黑洞(2 <= N <= 12, N为even)具象化在了他的农场里,每个都有明确的坐标位置。
根据他的计算,李宗泽知道将会形成N/2对连接起来的黑洞。如果黑洞A和B被连成一对,那么任何物体进入黑洞A,将会以进入黑洞A的方向从黑洞B中出来;进入黑洞B,也会以进入时的方向从黑洞A中出来。举例来说,黑洞A在(0,0),黑洞B在(1,0),牛玉鑫从(1/2,0)开始向X轴正方向移动,进入黑洞B,从黑洞A中出来,将继续向X轴正方向移动,再次进入黑洞B,被困在一个循环里。
李宗泽知道每一个黑洞在他的农场上的具体坐标,牛玉鑫只会向X轴正方向移动,但却不知道牛玉鑫目前的位置。
请你帮助李宗泽计算共有多少种黑洞配对方法会使在不幸的位置的牛玉鑫陷入循环。

【输入】

第一行:一个正整数N;
第二到N+1行:每行两个整数X,Y描述一个黑洞的位置。

【输出】

第一行:一个数,代表所有的会让牛玉鑫陷入循环的黑洞配对方法数。

【输入输出样例1】

样例输入:(wormhole.in)

4
0 0
1 0
1 1
0 1

有4个黑洞,形成一个正方形的循环。
样例输出:(wormhole.out)

2

给这4个黑洞编号为1…4。如果将1和2相连,3和4相连,牛玉鑫从1和2之间或3和4之间出发时会陷入循环。相同的,如果连接1和3,2和4,牛玉鑫也会陷入循环。只有连接1和4,2和3,牛玉鑫从任何一个位置开始移动都不会陷入循环。

【数据说明】

每个坐标在0~1,000,000,000内。
【解析】
点数不多,显然是暴力枚举所有配对方案。
1)方案一共多少?
N个虫洞保持原顺序不变,我们要配出N/2对来。
第一对,我们定为1号虫洞和任意另一个虫洞,共N-1种;
第二对,我们定为剩下的虫洞中编号最小的虫洞和任意另一个虫洞,共N-3种;
第三对,同理,共N-5种……
以此类推,直至配出N/2种。我们来数一数,配对总方案数Sum=(N-1)×(N-3)×…×3*1,取N最大值12,则有Sum最大值10395,并不大。
2)如何判断有环?
我们先排序,行小的放前面,行相同的按列数排,再预处理出每个点与它同行的,右边的,离它最近的点(为了方便,我们称这个点为next),然后我们枚举每一个起点,再开一个数组标记,如果next
根据配对方案,看是否能一直走下去,走了N次后,还可以一直走下去,这种方案肯定是可行的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值