P3226 [HNOI2012]集合选数 题解
纪念一下30紫and500AC
首先先膜拜一下神仙出题人,这题太神仙了。
题意:
要构造一个集合,使得 $ x \in A$ ,满足 $ 2x \notin A$ 且 $ 3x \notin A$ , 求 \(\{1,2,\ldots ,n\}\) 有多少个满足上述约束条件的子集。
思路
看到这题,第一个需要解决的问题时:这是啥题?数学?OEIS?
都不是。显然如果用数学比较难搞。这题是个神仙构造题。
参考小学奥数,考虑构造出这样子的矩阵:
1 2 4 8 16 ...
3 6 12 24 ...
9 18 36 72...
....
发现这要选了其中一个数,那么就不能取它上面和下面的数了。
那究竟有多少种方案呢?显然可以用状压DP解决。
但有一个漏洞,在上述矩阵中,5并没有出现过。所以我们需要对于5再构造一个矩阵,以此类推,最后用乘法原理求出答案。
神仙构造题!!赞美出题人!!
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
static char buf[1000000],*p1=buf,*p2=buf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
inline int read(){int x=0,f=1;char c=getchar();while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}while(c>='0' && c<='9') {x=x*10+c-48;c=getchar();}return x*f;}
inline void write(int x){static char buf[20];static int len=-1;if(x<0)putchar('-'),x=-x;do buf[++len]=x%10,x/=10;while(x);while(len>=0)putchar(buf[len--]+'0');}
const int mod = 1e9+1;
const int MAX = 1e5+10;
bool vis[MAX];
int a[30][30];
int n;
int f[30];
int dp[20][1<<18];
void build(int x){
a[1][1] = x;
for(int i = 2; i <= 17; i++){
a[i][1] = a[i-1][1] * 3;
// a[i][1] = min(a[i][1], (long long)(0x3f3f3f3f));
// a[i][1] %= mod;
if(a[i][1] > n) a[i][1] = 0x3f3f3f3f;
}
for(int i = 1; i <= 17; i++){
for(int j = 2; j <= 17; j++){
a[i][j] = a[i][j-1] * 2;
// a[i][j] = min(a[i][j], (long long)(0x3f3f3f3f));
// a[i][j] %= mod;
if(a[i][j] > n) a[i][j] = 0x3f3f3f3f;
}
}
for(int i = 1; i <= 17; i++) for(int j = 1; j <= 17; j++) vis[min(n+1, a[i][j])] = 1;
}
int solve(){
for(int i = 1; i <= 17; i++){
f[i] = 0;
for(int j = 1; j <= 17; j++) if(a[i][j] <= n) f[i] = (f[i] << 1) + (bool)(a[i][j]);
}
for(int i = 0; i <= 17; i++){
for(int j = 0; j <= f[i]; j++){
dp[i][j] = 0;
}
}
dp[0][0] = 1;
for(int i = 0; i <= 17; i++){
for(int j = 0; j <= f[i]; j++){
if(dp[i][j]){
for(int k = 0; k <= f[i+1]; k++) if(!(k & (k<<1)) and !(j&k)) dp[i+1][k] += dp[i][j], dp[i+1][k] %= mod;
}
}
}
return dp[17][0];
}
signed main(){
n = read();
int ans = 1;
for(int i = 1; i <= n; i++){
if(!vis[i]) build(i), ans *= solve(), ans %= mod;
}
write(ans);
return 0;
}