Codeforces Round #632 (Div. 2) D. Dreamoon Likes Sequences (思维 + 乘法原理)

思维+乘法原理

题意:

给出一个上限 d d ,和一个模数 m m ,计算符合下列条件的数列 a a 的个数:

  1. 数列 a a 的长度大于等于 1 1
  2. 数列 a a 的最小值大于等于 1 1 ,最大值小于等于 d d ,且是严格单调递增的。
  3. 对于 a a 用如下方式构造一个数列 b b , 要求构造的数列 b b 是严格单调递增的。
    b 1 = a 1 b_1 = a_1
    b i = b i 1 a i ( i 1 ) b_i = b_{i -1} \bigoplus a_i (i \geq 1)

题解:

参考官方题解:
首先定义一个函数 h ( a i ) h(a_i) ,表示 a i a_i 的二进制表示中数位为1的最高位是哪一位。比如 h ( 10 ) = 3 h(10) = 3 。由a是严格单调递增的条件我们可以知道, h ( a i + 1 ) > = h ( a i ) h(a_{i+1}) >= h(a_i)
然后我们利用b是严格单调递增的条件, b 2 = a 1 a 2 , b 2 > b 1 b_2 = a_1 \bigoplus a_2, b_2 > b_1 。可以知道 h ( a 2 ) > h ( a 1 ) h(a_{2}) > h(a_1) ,如果 h ( a 2 ) = h ( a 1 ) h(a_{2}) = h(a_1) 的话,那么异或之后这个最高位将会变成0,导致 b 2 < b 1 b_2 < b_1
同时我们也可以得出 h ( b 2 ) = h ( a 2 ) h(b_2) = h(a_2) 。依次类推就可以得出 h ( b i ) = h ( a i ) , h ( a i + 1 ) > h ( a i ) h(b_i) = h(a_i), h(a_{i+1}) > h(a_i)

每一个 h ( a i ) h(a_i) 都是不同的,且是严格递增的。那么在符合条件的数列中,只有一个 a i a_i 或者没有 a i a_i 满足 h ( a i ) = v h(a_i) = v 。也就是只有一个或者没有 a i a_i 属于 [ 2 v , m i n ( 2 v + 1 1 , d ) ] [2^v, min(2^{v +1} - 1, d)] 。只要从每一个数段中选出一个数,或者不选,这样组成的数列从小到大排序都是合法的。每一段有 m i n ( 2 v + 1 1 , d ) 2 v + 2 min(2^{v+1} - 1, d) - 2^v+2 个选择。根据乘法原理就可求出总数,最后要减去每一个都不选形成的数列。

代码:

/**
* Author : Xiuchen
* Date : 2020-04-07-12.02.25
* Description : 乘法原理
*/
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
//const int maxn = ;
int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}
int t;
ll d, m;
int main(){
    cin >> t;
    while(t--){
        cin >> d >> m;
        ll ans = 1 % m;
        for(int i = 0; i <= 31; i++){
            ll tmp = 1 << i;
            if(tmp > d) break;
            ll num = min((1ll << (i + 1)) - 1, d) - tmp + 2;
            ans = ans * num % m;
        }
        cout << (ans - 1 + m) % m << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44607936/article/details/105361695