LGP3226 [HNTS 2012] 集合选数 学习笔记
LGP3226 [HNTS 2012] 集合选数 学习笔记
题意简述
给定一个整数 \(n\)。问对于正整数集合 \(\{1,2,\dots,n\}\),其有多少个子集 \(s\) 满足:\(\forall x\in s\),\(2x,3x\notin s\)。
\(1\le n\le 10^5\)。
做法解析
妙题。
对于每一个与 \(2,3\) 互质的整数 \(u\),我们考虑一个矩形 \(A_u\),满足 \(a_{i,j}=2^i\times 3^j\)。原限制转化为不能选此矩形中相邻的两个数。原问题转化为求所有 \(A_u\) 的选法数量的乘积。
我们怎么求每一个 \(A_u\) 的方案数呢?状压就没了。时间复杂度能过。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
using namespace omodint;
using mint=modint<int(1e9+1)>;
const int MaxN=1e5+5,MaxS=(1<<20);
int N,vld[MaxS],alf,lim[12];mint dp[12][MaxS];
mint solve(int x){
static int n,m[12],a[12][20];
for(int i=1;i<=alog(N,3)+1;i++){
a[i][1]=(i==1?x:a[i-1][1]*3);
if(a[i][1]>N)break;n=i,m[i]=1;
for(int j=2;j<=log2(N/i)+1;j++){
a[i][j]=a[i][j-1]*2;
if(a[i][j]>N)break;m[i]=j;
}
lim[i]=(1<<m[i])-1;
}
mint res=0;
for(int i=0;i<=lim[1];i++)dp[1][i]=vld[i];
for(int i=2;i<=n;i++){
for(int j=0;j<=lim[i];j++){
if(!vld[j])continue;dp[i][j]=0;
int u=lim[i-1]-j;
for(int k=u;;k=u&(k-1)){
if(vld[k])dp[i][j]+=dp[i-1][k];
if(k==0)break;
}
}
}
for(int i=0;i<=lim[n];i++)res+=dp[n][i];
return res;
}
mint fans;
int main(){
readi(N),fans=1,alf=(1<<(int(log2(N))+1))-1;
for(int i=0;i<=alf;i++)vld[i]=(((i<<1)&i)?0:1);
for(int i=1;i<=N;i++)if(i%2&&i%3)fans*=solve(i);
writi(miti(fans));
return 0;
}
浙公网安备 33010602011771号