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

思维+乘法原理

题意:

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

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

题解:

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

每一个 h ( a i ) h(a_i) h(ai)都是不同的,且是严格递增的。那么在符合条件的数列中,只有一个 a i a_i ai或者没有 a i a_i ai满足 h ( a i ) = v h(a_i) = v h(ai)=v。也就是只有一个或者没有 a i a_i ai属于 [ 2 v , m i n ( 2 v + 1 − 1 , d ) ] [2^v, min(2^{v +1} - 1, d)] [2v,min(2v+11,d)]。只要从每一个数段中选出一个数,或者不选,这样组成的数列从小到大排序都是合法的。每一段有 m i n ( 2 v + 1 − 1 , d ) − 2 v + 2 min(2^{v+1} - 1, d) - 2^v+2 min(2v+11,d)2v+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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值