1. 程式人生 > >Codeforces 388 D. Fox and Perfect Sets

Codeforces 388 D. Fox and Perfect Sets

clu turn char 解題思路 href span void 題目 long

$ >Codeforces \space 388 D. ?Fox?and?Perfect?Sets<$

題目大意 :
定義一個完美的集合 \(S\) ,當且僅當 \(S\) 非負非空,且 \(\forall a, b \in S, a\text{ xor } b \in S\) ,求集合內最大元素不超過 \(n\) 的完美集合數量

\(1 \leq n \leq 10^9\)

解題思路 :

一個完美的集合等價於某個線性基能表示出的所有元素,所以問題轉化為對能表示的最大元素 \(\leq n\) 的線性基計數

考慮一個集合的線性基可能有很多種,但是必然存在一種方案使得基上的二進制位表示出來的元素是最大元素,不妨直接對滿足這種特征的線性基計數

考慮某一個二進制位如果在基中,那麽基中其他位的數都不能有這個位,反之基中所有比它高的位都的數都可以有這個位

如果不考慮 \(n\) 的限制的話,可以得出一個簡單的 \(dp[i][j]\) 表示從高到低第 \(i\) 個二進制位,已經選了 \(j\) 個二進制位作為基的方案數

根據上面推導可以得到 \(dp[i][j] = dp[i-1][j] \times 2^j + dp[i-1][j-1]\) (選這位為基/不選)

實際上如果有 \(n\) 的限制的話,是要滿足有 \(1\) 的二進制位組成的二進制數不能超過 \(n\) ,可以直接套一個數位 \(dp\)

具體的說,\(dp[i][j][0/1]\)

表示從高到低第 \(i\) 個二進制位,已經選了 \(j\) 個二進制位作為基,當且有 \(1\) 的位是否是 \(n\) 在二進制下的前綴

根據當前 \(n\) 的二進制位是 \(0\) 還是 \(1\) 分討轉移即可


/*program by mangoyang*/
#include<bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}

#define int ll
const int Mod = 1e9+7;
int s[55], f[55][55][2], len, ans; 
inline void up(int &x, int y){ x = (x + y % Mod) % Mod; }
signed main(){
    int x; read(x); if(!x) s[++len] = 0;
    while(x) s[++len] = x % 2, x /= 2;
    reverse(s + 1, s + len + 1);
    f[0][0][1] = 1;
    for(int i = 1; i <= len; i++)
        for(int j = 0; j <= len; j++){
            if(j) up(f[i][j][0], f[i-1][j-1][0]);
            up(f[i][j][0], f[i-1][j][0] * (1ll << j));
            int x = (j ? (1ll << j - 1) : 1) % Mod;
            int y = (j ? (1ll << j - 1) : 0) % Mod;
            if(s[i] == 0) up(f[i][j][1], f[i-1][j][1] * x);
            else{
                if(j) up(f[i][j][1], f[i-1][j-1][1]);
                up(f[i][j][1], f[i-1][j][1] * y);
                up(f[i][j][0], f[i-1][j][1] * x);
            }
        }
    for(int i = 0; i <= len; i++)
        up(ans, f[len][i][0]), up(ans, f[len][i][1]);
    cout << ans;
    return 0;
}

Codeforces 388 D. Fox and Perfect Sets