Codeforces Round #631 (Div. 2) D. Dreamoon Likes Sequences

7 篇文章 0 订阅

题面

传送门
大概题意就是给出一个d和m。
求满足以下条件的数组的数量模m。

  1. 数组长度大于等于1
  2. 数组所有元素大于等于1,小于等于d
  3. 数组严格递增
  4. 衍生数组( b1=a1, ∀i>1,bi=bi−1⊕ai)严格递增

思路

定义f(x)为x的下一位最小值
打表可得f(1)=2,f(2)=f(3)=4,f(4)=f(5)=f(6)=f(7)=8…
至于原因就是一个数若异或上自己最高也是1的那么那一位就会被抵消从而小于自己。比如2的下一位不能3因为他们都具有2的1次,异或后便无,所以会小于2的1次,所以2的下一位不能是3,其他同理。而异或上比自己高一位的,那一位必不会抵消所以一定大于本身。

思考一下
如果数组长度为1,那么就是1-d所有数。

如果数组长度为n,那么则是在所有长度为n-1的数组的基础上添加一个可行的数。
那么我们只需记录结尾在f(i)里的数有多少个,就可以求得个数*可行数,再转移到新结尾即可
而又因为每次下一个数都是2的幂次的跳,所以长度不会超过log
下一个可行数个数怎么计算,假设当前数x的层数为i(2i<=x<2i+1),则下一位至少要大于2i+1,所以个数为d-2i+1+1。
如何转移,如果d无穷大,那么第i层会得到2i个数,因为d不是无穷大,所以只有最后一层要特殊处理一下,个数为d-2i+1+1。
对了 避免转移出错,应该倒序遍历。
复杂度大概在O(log3N)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long, long> pll;
const int maxn = 3e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
ll t, d, m;
ll dep[32];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> t;
    while (t--) {
        cin >> d >> m;
        for (ll i = 0; i < 32; ++i) {
            if ((1ll << i + 1) > d) {
                dep[i] = d - (1 << i) + 1;
                break;
            }
            dep[i] = (1 << i);
        }
        ll ans = d;
        int times = 32;
        while (times--) {
            for (int i = 31; i >= 0; --i) {
                if (d - (1ll << (i + 1)) + 1 <= 0) continue;
                ans += dep[i] * (d - (1ll << i + 1) + 1) % m;
                ll k = i + 1;
                while ((1ll << (k + 1)) <= d) {
                    dep[k] += (1 << k) * dep[i];
                    dep[k] %= m;
                    k++;
                }
                dep[k] += (d - (1 << k) + 1) * dep[i];
                dep[k] %= m;
                dep[i] = 0;
            }
        }
        cout << ans % m << endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值