Blah数集

GitHub跳转原题关系图返回列表

题意与原解析均从本地 OpenJudge 缓存迁移。

OJ: noi_openjudge

题目 ID: ch0304-2729

难度:未知

标签:

日期: 2026-07-30 23:01

题意

完整题面见同目录的 problem.md

思路

解析

经过普通队列输出数据后,发现普通队列不正正确

发现由44生成的99比较33生成的1010要大,根据做题目的经验,这个题目肯定有数学规律, 进一步输出数据,找规律.

根据数据范围,知道,最终的代码一定是O(n)O(n)的,O(nlogn)O(nlogn)都不行,所以不能使用优先队列,这暗示我们,产生的数据可以按某种方式有序,不需要进行比较.

  1. 每一次产生的两个数a<ba < b 一定成立
  2. 每一次都应该由最小的数首开两个新的数a,ba,b

每一次产生的两新的数,和原来的已经产生的数,有什么关系,

使用数学归纳法证明

xa,ybx \geqslant a,y \geqslant b,且取t=min(a,b)t = min(a,b),且tzt \leqslant z,zz 是生成x,yx,y的元素

x=2×z+1y=3×z+1 x = 2 \times z +1 \\ y = 3 \times z +1 \\

现在取tt,得到A=2×t+1,B=3×t+1A = 2 \times t + 1, B = 3\times t +1,显然

Ax2×t+12×z+1By3×t+13×z+1 A \leqslant x \Rightarrow 2\times t + 1 \leqslant 2\times z +1 \\ B \leqslant y \Rightarrow 3\times t + 1 \leqslant 3\times z +1

显然成立

这就说明如果原来的双队列成立(单个队列都是单调的),则从取双队列头的最小值,产生新的值,然后把值各自加入到队列尾部, 得到的新的双队列也成立.

错误的代码

使用优先队列,超时.

代码

cpp
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e5+5;

template<typename T = int,int siz = maxn>
struct myqueue{
    T a[siz+5];
    //tail 指向最后一个元素后面一个位置
    //head 指向第一个元素
    int head = 0,tail=0; 

    void clear() { head =tail = 0;}

    void push(T b) { a[tail++] = b;}

    void pop(){head++;}
    void pop_back(){tail--;}

    T front() { return a[head];}
    T back() { return a[tail-1];}

    bool empty() { return head == tail;}

    int size() { return tail-head;}
};

typedef  long long ll;
myqueue<ll> a;
myqueue<ll> b;
int s,n;


int main() {
    while (cin >> s >> n) {
        //特别的处理第一个元素.
        int cnt = 1;
        a.clear();
        b.clear();
        ll t = s;
        if( cnt >= n) {
            cout << t <<endl;
            continue;
        }
        a.push(2*t+1);
        b.push(3*t+1);
        int pre = t;
        while(1) {
            ll t1 = 0x7f7f7f7f7f7f7f7f;
            ll t2 = 0x7f7f7f7f7f7f7f7f;

            if( !a.empty()) t1=a.front();
            if( !b.empty()) t2=b.front();

            if( t1 <= t2) {
                t = t1;
                a.pop();
            }
            else
            {
                t= t2;
                b.pop();
            }
            //如果和上一个取的数一样大,
            // 根据集合中元素不能一样,所以这个不能计算
            if( t == pre) continue;
            cnt++;
            pre = t;
            // cout << cnt << " " << t<< "\n";
            if( cnt == n) {
                cout << t << endl;
                break;
            }
            if(a.empty() ||2*t+1 != a.back() )
                a.push(2*t+1);
            if(b.empty() ||3*t+1 != b.back() )
                b.push(3*t+1);
        }
    }
    int m = a.tail;
    if( m < b.tail)
        m = b.tail;
        
    // for(int i =0;i<m;i++)
    // {
    //     cout << a.a[i] << " " << b.a[i] << endl;
    // }

    return 0;
}

复杂度

总结