P5968 [POI2017]Reprezentacje ró?nicowe 暴力+二分

3 篇文章 0 订阅
1 篇文章 0 订阅

题意:

给定一个数列 a:

  • n ≤ 2 n\le 2 n2 时,a_n=na**n=n
  • n > 2 n>2 n>2,且 n 是奇数时, a n = 2 × a n − 1 a_n=2\times a_{n-1} an=2×an1
  • n > 2 n>2 n>2,且 n 是偶数时, a n = a n − 1 + r n − 1 a_n=a_{n-1}+r_{n-1} an=an1+rn1

其中 r n − 1 = mex ⁡ ( ∣ a i − a j ∣ ) ( 1 ≤ i ≤ j ≤ n − 1 ) m e x ( S ) r_{n-1}= \operatorname{mex}(|a_i-a_j|)(1\le i\le j\le n-1)mex(S) rn1=mex(aiaj)(1ijn1)mex(S) 表示最小的不在 S集合里面的非负整数。

数列 a 的前若干项依次为:

1,2,4,8,16,21,42,51,102,112,224,235,470,486,972,990,19801,2,4,8,16,21,42,51,102,112,224,235,470,486,972,990,1980。

可以证明,对于任意正整数 xx,只存在唯一一对整数 (p,q)满足 x = a p − a q x=a_p-a_q x=apaq,定义为 repr ⁡ ( x ) \operatorname{repr}(x) repr(x)

比如 repr ⁡ ( 17 ) = ( 6 , 3 ) \operatorname{repr}(17)=(6,3) repr(17)=(6,3) repr ⁡ ( 18 ) = ( 16 , 15 ) \operatorname{repr}(18)=(16,15) repr(18)=(16,15)。 现有 n 个询问,每次给定一个正整数 x,请求出 repr ⁡ ( x ) \operatorname{repr}(x) repr(x)

分析:

第一眼,不会做/bushi

仔细观察题面,我们可以发现,偶数位置的数增长极快,因为 a 57 a_{57} a57就已经超过1e9了。而且对于大小超过1e9的数,奇数位和前一项偶数位的差也大于1e9,所以57位之后的数,能组成题目要求的repe函数的只有偶数项和前一位奇数项。

所以我们可以暴力打出前57位能形成的数,对于查询,如果在前面57项里查到了就输出。否则的话,需要二分在表里找到最大的小于他的数,并得到他是在表里的第几个。因为r[]是递增的,那么如果当前x代表了一个r[i],也就意味着前面的x-1个数都已经被别的数对所代表了。又因为表里有num个,这是前57项里有的。那么x-num就是从57项后的第几个数对有这样一个差x。那么每一个数对2个数,又因为这样的计算包含了最后差等于x的那个数对,所以答案就是

( 57 + ( x − n u m ) ∗ 2 − 1 , 57 + ( x − n u m ) ∗ 2 − 2 ) ( 57+(x-num)*2-1,57+(x-num)*2-2 ) 57+(xnum)21,57+(xnum)22

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	map <int ,pair<int ,int > > s;
	typedef map<int ,pair<int ,int > >::iterator it;
	int t,n;
	int a[100],b[10000],cnt;
	 
	void work()
	{
	    a[1]=1,a[2]=2;
	    s[1]=make_pair(2,1);
	    for(n=3;;n++)
	    {
	        if(n&1) a[n]=a[n-1]*2;
	        else
			{
				for(int j=1;;j++)
				{
					if(!s.count(j))
					{
					 a[n]=a[n-1]+j;
					 break; 
					}
				} 
			} 
	        for(int j=1;j<n;j++)
			{
				s[a[n]-a[j]]=make_pair(n,j);
			}
	        if((!(n&1))&&a[n]>1e9) break;
	    }
	    for(it l=s.begin();l!=s.end();l++)
	    {
	    	b[++cnt]=l->first;
		}
	    scanf("%d",&t);
	    while(t--)
	    {
	        int x;
	        scanf("%d",&x);
	        it l=s.find(x);
	        if(l!=s.end())
	        {
	        	printf("%d %d\n",l->second.first,l->second.second);
			}
	        else
	        {
	            int y=lower_bound(b+1,b+cnt+1,x)-b-1;
	            printf("%d %d\n",n+(x-y)*2,n+(x-y)*2-1);
	        }
	    }
	}


}

int main()
{
	zzc::work();
	return 0;
 } 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值