題解 [HNOI2012]集合選數
阿新 • • 發佈:2020-10-22
題目大意
直接看題面吧。
思路
感覺挺水的一道題啊?怎麼評到紫色的啊?考試的時候LJS出了這個題的加強版我就只想出這個思路,然後就爆了。。。
不難發現,我們可以構造矩陣:
x 2x 4x 6x ...
3x 6x 12x 24x 48x ...
9x 18x 36x ...
然後實際上就相當於在這個矩陣中選出一些數使得兩兩不相鄰。因為行數列數都是 \(\log\) 級別的,所以直接狀壓就好了。
\(\texttt{Code}\)
#include <bits/stdc++.h> using namespace std; #define Int register int #define mod 1000000001 #define MAXN 1000005 template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;} template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);} template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');} template <typename T> void Mx (T &a,T b){a = max (a,b);} template <typename T> void Mi (T &a,T b){a = min (a,b);} bool mark[MAXN]; int n,a[25][25],lim[25]; void init (int x){ a[1][1] = x; for (Int i = 1;i <= 15;++ i){ if (i > 1) a[i][1] = a[i - 1][1] * 3; if (a[i][1] > n) break; for (Int j = 2;j <= 20;++ j){ a[i][j] = a[i][j - 1] * 2; if (a[i][j] > n){ lim[i] = j - 1; break; } } for (Int j = 1;j <= lim[i];++ j) mark[a[i][j]] = 1; } } bool chk[1 << 21]; int dp[2][1 << 22]; int WorkDP (){ int endl = 0; for (Int i = 0;i < (1 << lim[1]);++ i) dp[1][i] = chk[i]; for (Int i = 2;i <= 15;++ i){ if (a[i][1] > n){ endl = i - 1; break; } for (Int S = 0;S < (1 << lim[i]);++ S) if (chk[S]){ dp[i & 1][S] = 0; for (Int S1 = 0;S1 < (1 << lim[i - 1]);++ S1) if ((S & S1) == 0) dp[i & 1][S] += dp[i - 1 & 1][S1],dp[i & 1][S] %= mod; } else dp[i & 1][S] = 0; } int ans = 0; for (Int S = 0;S < (1 << lim[endl]);++ S) ans += dp[endl & 1][S],ans %= mod; return ans; } signed main(){ read (n);int res = 1; for (Int i = 0;i < (1 << 20);++ i) chk[i] = ((i & (i >> 1)) == 0); for (Int i = 1;i <= n;++ i) if (!mark[i]) init (i),res = 1ll * res * WorkDP () % mod; write (res),putchar ('\n'); return 0; }