P8873 [传智杯 #5 初赛] E-梅莉的市场经济学

题目背景

梅莉这个学期选修了经济学。但是主修心理学的她实在不擅长经济领域的分析,为此她时常抱怨自己学不会,想退课。

但是如果现在退掉的话这学期的学分就不够啦,因此她根据“梦中”的经历,“胡诌”了一个简单到不现实的市场模型,并依据这个模型编起了 essay。为了方便地编出图表,她需要写一个程序来查询每个时刻的市场贸易差。

题目描述

市场每一天的贸易差可以视为一个有周期性规律的数列 a [0],[0,1,0,−1,0],[0,1,2,1,0,−1,−2,−1,0],[0,1,2,3,2,1,0,−1,−2,−3,−2,−1,0] 具体而言,a 可以被分为无穷段,第 i 段的内容为{0,1,,i,i1,,0,−1,,i,i+1,0}。如下图所示,是将 a 数列内的前一些点绘制在坐标轴上的情况:

现在梅莉对市场发起了 q 次询问,每次她会给定一个 k,希望求出 a_{k}​ 是多少。

输入格式

  • 第一行有一个正整数 q,表示询问次数。
  • 接下来 q 行,每行一个正整数 k,描述每次询问。

输出格式

  • 输出共 q 行。对于每次询问,输出对应的结果。
  • 输入输出样例

    输入 

    9
    1
    10
    100
    1000
    10000
    100000
    1000000
    10000000
    100000000

    输出 #1复制

    0
    1
    6
    -9
    -11
    -128
    406
    1629
    5154

题解:

数学题。

由题目描述可以发现每一段的个数为 1 , 5 ,9 , 13 ...

可以得到第n段的个数为     b_{n}=1+(n-1)*4

前n项和                            S_{n}=2*n*n-n

当给定数字k时,应找到k所在的段中,再在该段中寻找结果。

最简单粗暴的方式就是通过1开始枚举,直至找到第一个i使得2*i*i-i>k时,可以得出i-1即为k所在的段,但这样的方法显然时间复杂度太大。

所以换个思路,我们可以从 S_{n}=2*n*n-n入手,

S_{n}=a,解得

                            n=\sqrt{\frac{a}{2}+\frac{1}{16}}+\frac{1}{4}

  再对n取整后可得当前为第n+1段,但是如果n求出来本身就是整数呢(即它为当前段的最后一个)?这时他所对应的即为第n段,于是我们进行如下处理:当算出的n满足2*n*n-n==a时,我们让n--,这样之后可以确保它一定是在第n段的后一段。

  由此我们可以知道第n段的范围是S_{n-1}+1S_{n}之间,这一部分代码如下:

t=(sqrt(a/2.0+1/16.0)+1/4.0)*1;
if(t*t*2-t==a)t--; 
st=2*t*t-t+1;
t++;
en=2*t*t-t;

 (st为这一段的第一个位置,en为这一段的最后一个位置)

由此我们已经知道了k所在的位置是在 st 和 en 之间。

 再观察图可以发现在第n段中,它总是先从0,开始一个一个增加到n-1之后再开始一个一个减小到   -(n-1)为止,之后再增到0,这时这一段结束。

可以发现每一段的图像都关于 st 和 en 的中点(mid)对称,即 

                                       a_{st+i}=-a_{mid+i}    (st\leq st+i\leq mid)

  由此可以再定义一个整型变量fu初始值为1,当k>mid时fu=-1。这样可以将正负两部分图像转换为只有正的这一部分上。

 同时st和mid之间,又关于直线x=midd对称(midd为st的mid的中点),由此有

                                            a_{st+i}=a_{midd+i}      (st\leq st+i\leq midd)

由图像可知,st\leq st+i\leq midd时,横坐标与纵坐标的增量均为1,

a_{st}=0,a_{st+1}=1,a_{st+2}=2....从而有a_{st+i}=i

此部分代码为:

    long long int i,q=0,st,en,mid,midd,fu=1;
	long long int t,l;
	t=(sqrt(a/2.0+1/16.0)+1/4.0)*1;
	if(t*t*2-t==a)t--; 
	st=2*t*t-t+1;
	t++;
	en=2*t*t-t;
	mid=(st+en)/2;
	if(a>mid)
	{
		a=(a-mid)+st;
		fu=-1;
	}
	midd=(st+mid)/2;
	if(a<midd)cout<<(a-st)*fu<<endl;
	else cout<<(mid-a)*fu<<endl;

 综上,本题代码如下:

#include<bits/stdc++.h>
using namespace std;
void init(long long int a)
{
	long long int i,q=0,st,en,mid,midd,fu=1;
	long long int t,l;
	t=(sqrt(a/2.0+1/16.0)+1/4.0)*1;
	if(t*t*2-t==a)t--; 
	st=2*t*t-t+1;
	t++;
	en=2*t*t-t;
	mid=(st+en)/2;
	if(a>mid)
	{
		a=(a-mid)+st;
		fu=-1;
	}
	midd=(st+mid)/2;
	if(a<midd)cout<<(a-st)*fu<<endl;
	else cout<<(mid-a)*fu<<endl;
}
int main()
{
    int n;
    long long int a;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a;
		if(a==1){
			cout<<"0"<<endl;
		}else init(a);
	 } 
    return 0;
}

水平有限,如有错误,欢迎指出!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值