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


    题意:

    给定一个数列 a:

    • \(n\le 2\) 时,a_n=na**n=n
    • \(n>2\),且 n 是奇数时, \(a_n=2\times a_{n-1}\)
    • \(n>2\),且 n 是偶数时,\(a_n=a_{n-1}+r_{n-1}\)

    其中 \(r_{n-1}= \operatorname{mex}(|a_i-a_j|)(1\le i\le j\le n-1)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\),定义为 \(\operatorname{repr}(x)\)

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

    分析:

    第一眼,不会做/bushi

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

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

    \[( 57+(x-num)*2-1,57+(x-num)*2-2 ) \]

    代码:

    #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;
     } 
     
    
  • 相关阅读:
    DriveInfo 类 提供对有关驱动器的信息的访问
    遍历数组 例子
    怎么判断点击dataGridView1的是第几列
    无法加载协定为“ServiceReference1.LanguageService”的终结点配置部分,因为找到了该协定的多个终结点配置。请按名称指示首选的终结点配置部分。
    c#面试题及答案(一)
    SQL杂谈 ,有你想要的...
    TextView和Button的学习
    GitHub的学习和使用
    App的布局管理
    EditText制作简单的登录界面
  • 原文地址:https://www.cnblogs.com/youth518/p/13664763.html
Copyright © 2020-2023  润新知