*题解:P3226 [HNOI2012] 集合选数
解析
注意到题面里的课程名称为《集合论与图论》,考虑对于每个 \(x\),向 \(2x\) 和 \(3x\) 连边,当 \(n = 18\) 时,画出来的图应该是这样的:

这样看的话有 \(O(\log_2 n)\) 行与 \(O(\log_3 n)\) 列,问题变为炮兵阵地弱化版,可以使用状压 DP 来解决。
时间复杂度 \(O(n\log n + k ^ 2 \log n)\)。其中 \(k\) 表示从序列中选取的元素互不相邻的方案数。
代码
#include<bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
#define getchar() getchar_unlocked()
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> pii;
const int N = 1e5 + 5,M = 18,mod = (int)1e9 + 1;
vector<vector<int> > v[N];
int f[M][(1 << M) + 5];
vector<int> num[M];
bool vis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out1.txt","w",stdout);
int n;
cin>>n;
for(int i=1;i<M;i++){
for(int j=0;j<(1 << i);j++){
bool flag = true;
for(int k=0;k<i;k++){
if((j & (1 << k)) && (j & (1 << (k + 1)))){
flag = false;
break;
}
}
if(!flag) continue;
num[i].push_back(j);
}
}
int cnt = 0;
for(int i=1;i<=n;i++)if(!vis[i]){
cnt++;
for(int j=1;i * j <= n;j *= 2){
v[cnt].push_back({});
for(int k=1;i * j * k <= n;k *= 3){
vis[i * j * k] = true;
v[cnt].back().push_back(i * j * k);
}
}
}
int res = 1;
for(int i=1;i<=cnt;i++){
for(int j : num[v[i][0].size()]){
f[0][j] = 1;
}
for(int l=1;l<(int)v[i].size();l++){
int now = v[i][l].size(),pre = v[i][l - 1].size();
for(int j : num[now]){
f[l][j] = 0;
for(int k : num[pre]){
bool flag = !(j & k);
if(flag){
f[l][j] = (f[l][j] + f[l - 1][k]) % mod;
}
}
}
}
int sum = 0;
for(int j : num[v[i].back().size()]){
sum = (sum + f[(int)v[i].size() - 1][j]) % mod;
}
res = 1ll * res * sum % mod;
}
cout<<res;
return 0;
}

浙公网安备 33010602011771号