1. 程式人生 > >jzoj5889 【NOIP2018模擬9.29】完美集合

jzoj5889 【NOIP2018模擬9.29】完美集合

題意

定義一個數集S是完美的,a,bSaxorbS任意a,b\in S,都有axorb\in S。求由1…n組成的完美數集有多少個。

分析

也就是求有多少個線性空間。 好吧說人話,首先要知道相同的數集插入線性基並回消後結果是唯一的。 一個完美集對應著他的線性基,一個線性基對應著一個完美集(也就是有2B2^{|B|}個數的完美集。B是基的大小。) 既然唯一對應,那麼問題就變成了構造線性基。 當n是2^n-1時,設f[i][j]f[i][j]表示從高位開始已經確定了I位的值,並且有j位有值。 那麼,當前這一位可選(方案數為1);可不選(方案數為2^j,前面的在這一位上可有可無。)

當n不是2^n-1時,要考慮大小問題。即線性基中能得出的最大數不能超過n。回消之後這個數是每一位上異或起來。再多開一維狀態表示當前是否頂著n了,不選的時候討論一下前面是放奇數個還是放偶數個就行。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define get(a) (((n) & (1<<(a)-1))!=0)
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
typedef long long ll; const int mo = 1e9 + 7, inv2 = 500000004; int n; ll f[33][33][2]; int main() { freopen("set.in","r",stdin); freopen("set.out","w",stdout); cin>>n; f[32][0][1] = 1; for (int i = 32; i; i--) { for (int j = 0; j <= 32 - i; j++) { for (int a = 0; a < 2; a++) if (f[i][j]
[a]){ int b = a && (get(i-1) == 0); f[i-1][j][b] = (f[i-1][j][b] + f[i][j][a] * max(1,(1<<j)/2) % mo) % mo; if (a == 0 || get(i-1) == 1) { b = a && (get(i-1) == 1); f[i-1][j][b] = (f[i-1][j][b] + f[i][j][a] * ((1<<j)/2) % mo) % mo; f[i-1][j+1][b] = (f[i-1][j+1][b] + f[i][j][a]) % mo; } } } } ll ans = 0; for (int i = 0; i <= 31; i++) ans = (ans + f[1][i][0] + f[1][i][1]) % mo; cout<<ans<<endl; }