题解:P11506 [ROIR 2017 Day 1] 校园

· · 题解

题目:[ROIR 2017 Day 1] 校园

题意

一个表格有 n 行,无限多列,在每一列中要求行数为 k 的倍数的格子里有 x 个数,其他各自中有 y 个数,格子中的数按照自然数顺序排序。共 q 次询问,求给出的数 a_1, a_2, \dots, a_q 在表格中的行数。

下图是一种 n = 7, k = 3, x = 2, y = 3 的情况,同题面样例:

思路

如果对于每一次询问,直接按照题意模拟填数的话,时间复杂度为 \text{O} (qn) ,显然只能通过 subtask #1 。

考虑优化,规定每填 k - 1y1x 为一个周期。

我们可以预处理出每一列填入数字的数量一个完整周期内填入数字的数量 S_TS_k ,如果 a_i > S_T , 那么 a_i 所在的行数与 {a_i}^{\prime} = a_i \bmod S_T ({a_i}^{\prime} < S_T) 所在的行数相同,可以缩小一定范围的常数。

接下来对于每次询问 {a_i}^{\prime} ,我们可以计算出其经历了几个完整的周期,以及周期之外最后剩余的行数。这些都可以通过数学方法直接处理,时间复杂度 \text{O} (q)

以样例为例, S_T = 8S_k = 19 ,若询问 a_1 = 50 ,那么 {a_1}^{\prime} = a_1 \bmod n = 12{a_1}^{\prime} 经历了 1 个完整的周期,最终剩余 2 行,所以最终答案为 3 + 2 = 5

核心判断语句如下:

ll query(ll a){
    if(a==0) return n;
    ll block=a/Sk,tmp=block*Sk;// block 代表经历了几个完整周期, tmp 代表前面的周期有多少个数
    //printf("%lld block:%lld tmp:%lld\n",a,block,tmp);
    a-=tmp;
    if(a<=(k-1)*y){//全 y
        if(a%y) return block*k+a/y+1;
        else return block*k+a/y;
    }else{//含有 x & y
        return block*k+k;
    }
}

代码

评测记录:https://www.luogu.com.cn/record/197131297

代码(C++):https://www.luogu.com/paste/0ulbm2nd

闲话

很好的签到题,注意 a_i \leq 10^{18} 要开 long long 。