zoukankan      html  css  js  c++  java
  • Codeforces 1330D

    Description

    思路

    异或又叫做半加,它与和加法的区别就是不会进位。可知
    (a+b=(a⊕b)+2(a&b))
    (2(a&b))就是进位的部分的值。

    对于这一题,(b_i=b_{i-1}⊕a_i)可以写成(b_i=b_{i-1}+a_i-2(a_i&b_{i-1}))

    因为(b_i)是单调递增的,所以有(b_i-b_{i-1}=a_i-2(a_i&b_{i-1})>0),即需要满足(a_i>2(a_i&b_{i-1}))

    如果(a_i&b_{i-1})这一项最高位的1和(a_i)的相等,那么(2(a_i&b_{i-1}))必定大于(a_i)

    所以(a_i&b_{i-1})最高位的1低于(a_i)的,即(b_{i-1})的最高位低于(a_i)的。

    因为(b_{i-1}=a_{1}⊕...⊕a_{i-1}),由归纳法可知道(a_i)最高位的1所在的位置是单调递增的。

    而且只要保证(a_i)位数严格递增,就必定满足(a_i>2(a_i&b_{i-1}))。当(a_i)位数为n时,它有(2^{n-1})种情况。

    所以按照位数递增来dp就好了。复杂度是(O((log_2n)^3))。未满最高位的部分单独处理。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 50;
     
    ll dp[N][N];
     
    void work(int n, ll m) {
        for(int i = 1; i <= n; i++) 
            dp[i][i] = 1;
        for(int b = 1; b <= n; b++) { //终点的位数
            for(int a = b - 1; a >= 1; a--) { //起点的位数
                dp[a][b] = 0;
                for(int i = a + 1; i <= b; i++) {
                    dp[a][b] += (1ll << (i - 1)) * dp[i][b] % m;
                    dp[a][b] %= m;
                }
            }
        }
    }
     
    int main() {
        ios::sync_with_stdio(false);
        int t;
        cin >> t;
        while(t--) {
            ll d, m;
            cin >> d >> m;
            int cnt = 0;
            ll td = d;
            while(td) {
                cnt++;
                td >>= 1;
            }
            work(cnt - 1, m);
            ll ans = 0;
            ll k = d - (1 << (cnt - 1)) + 1; //未满的部分单独处理
            for(int i = 1; i <= cnt - 1; i++) {
                for(int j = i; j <= cnt - 1; j++) {
                    ans += (((1ll << (i - 1)) * dp[i][j] % m) * k) % m;
                    ans %= m; 
                    ans += ((1ll << (i - 1)) * dp[i][j] % m) % m;
                    ans %= m; 
                }
            }
            cout << (ans + k) % m << endl;
        }
    }
    

    Update

    看了官方题解,才发现dp麻烦了。对于每个位数v,有区间([2v,min(2(v+1)−1,d)])那么多种数可以选择,即((min(2(v+1)−1,d)−2v+1)+1)种选择(不选这个区间的数也是一种选择,故加1)。全部乘起来再减1(减去全部不选的方案)就是答案。这样处理好简单。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 50;
    
    int main() {
        ios::sync_with_stdio(false);
        int t;
        cin >> t;
        while(t--) {
            int n, m;
            cin >> n >> m;
            ll ans=1;
            for(int i = 0; i < N; i++) {
                if(n < (1 << i)) break;
                ans = ans * (min((1 << (i+1)) - 1, n) - (1 << i) + 2) % m;
            }
            ans--;
            if(ans < 0) ans += m;
            cout << ans << endl;
        }
    }
    
  • 相关阅读:
    (转)Python格式化字符 %s %d %f
    (转) Linux Shell经典实例解析
    (转)用shell脚本实现杨辉三角的4个实例!
    (转)[Shell]tr命令详解
    (转)linux shell单引号、双引号及无引号区别
    (转)Linux基础------Shell数值计算的几种方法
    (转)Shell中获取字符串长度的七种方法
    (转)source、sh、bash、./执行脚本的区别
    session和cookie的联系
    javascript中的cookie
  • 原文地址:https://www.cnblogs.com/limil/p/12634359.html
Copyright © 2011-2022 走看看