日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[HNOI2012]集合选数(思维构造 + 状压dp)

發布時間:2023/12/3 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [HNOI2012]集合选数(思维构造 + 状压dp) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

problem

題目鏈接

solution

從最小一個數 xxx 開始,將其 2x,3x2x,3x2x,3x 放入,再將 2(2x),3(2x),2(3x),3(3x)2(2x),3(2x),2(3x),3(3x)2(2x),3(2x),2(3x),3(3x) 放入,以此類推 …\dots 將其合并為一個集合。重復又找一個最小未進入集合的數 xxx

顯然答案為若干個互不相交,并集為 [1,n][1,n][1,n] 的集合答案的乘積。

一個集合里面的數字是相互制約的,不難發現集合大小不超過 log?2n?log?3n\log_2n*\log_3nlog2?n?log3?n,大概就是 17×1117\times 1117×11

考慮怎么計算一個獨立集合的可選方案數。

很巧妙的是這個限制只有兩個(兩倍和三倍),在平面內可以構造出一個矩陣。

矩陣中 (i,j)(i,j)(i,j) 的右邊位置放其數值的三倍,下面位置放其數值的兩倍。

1 3 9 ...

2 6 18 ...

4 12 36 ...

那么答案就等價為在矩陣中隨機選任意個數,要求數兩兩不相鄰。

矩陣行列數都比較小,可以采取狀壓 dpdpdp 求解。

顯然某一行是否合法與矩陣真實長相無關,至于選擇的位置有關。

所以提前預處理一行的選擇狀態,判斷是否合法。不合法當且僅當有相鄰兩列都選了,用 i<<1&i 判斷。

之后枚舉當前行及操作狀態,在合法的基礎上,枚舉上一行的操作狀態,再判斷兩行之間沒有同一列都操作了,用 s&t=0 判斷。

能構造矩陣,完全是因為限制一個數的條件只有兩個,可以放在二維平面內。這完全就是很投巧的想法。

code

#include <bits/stdc++.h> using namespace std; #define mod 1000000001 #define int long long int N, n, m, ans = 1; bool vis[100005], g[1 << 20]; int lim[20], a[20][20], f[20][1 << 20];void init( int x ) {for( int i = 1;;i ++ ) {if( i == 1 ) a[i][1] = x;else a[i][1] = a[i - 1][1] << 1;if( a[i][1] > N ) break;else n = i;vis[a[i][1]] = 1;lim[i] = 2;for( int j = 2;;j ++ ) {a[i][j] = a[i][j - 1] * 3;if( a[i][j] > N ) break;else vis[a[i][j]] = 1, lim[i] = 1 << j;}} }int solve() {for( int i = 0;i < lim[1];i ++ ) f[1][i] = g[i];for( int i = 2;i <= n;i ++ )for( int s = 0;s < lim[i];s ++ ) {if( ! g[s] ) continue;f[i][s] = 0;for( int t = 0;t < lim[i - 1];t ++ )if( g[t] and ! (s & t) )f[i][s] = ( f[i][s] + f[i - 1][t] ) % mod;}int ret = 0;for( int i = 0;i < lim[n];i ++ )ret = ( ret + f[n][i] ) % mod;return ret; }signed main() {for( int i = 0;i < (1 << 19);i ++ )g[i] = ! (i << 1 & i);scanf( "%lld", &N );for( int i = 1;i <= N;i ++ )if( ! vis[i] ) init( i ), ans = ans * solve() % mod;printf( "%lld\n", ans );return 0; }

總結

以上是生活随笔為你收集整理的[HNOI2012]集合选数(思维构造 + 状压dp)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。