[BZOJ2734] [HNOI2012]集合选数
[BZOJ2734] [HNOI2012]集合选数
蒻得不行的我觉得这是一道比较难的题,以至于我卡了很久
可以看出,所有会互相直接造成影响的数之间构成一张\(DAG\),边就是\(i->i*2,i->i*3\)
取出每一个连通块之后,连了边的点不能同时选,就是一个独立集个数的问题
\(DAG\)还可以求独立集?
我们其实可以惊人得发现,这张\(DAG\)过于整齐,每个点都是至多两条出边,就是一个网格图,转化成一张网格图上相邻的点不能取的问题
这个,状压矩阵即可\(dp[i][S]\)
const int N=1e5+10,P=1e9+1;
int n;
ll dp[20][1<<11];
int A[20];
ll Solve(int i){
int t=0;
A[0]=0;
dp[0][0]=1;
for(;i<=n;i*=2) {//网格图的列数
t++;
A[t]=0;
int c=0;
for(int j=i;j<=n;j*=3) A[t]|=1<<(c++); //取出网格图这一行的大小
rep(j,0,A[t]) dp[t][j]=0;
rep(S1,0,A[t-1]) {
int fl=1;
rep(j,0,c+1) if((S1&(1<<j)) && (S1&(1<<(j+1)))) fl=0;
if(!fl) continue;
int R=A[t]^(S1&A[t]);
for(reg int S2=R;;S2=(S2-1)&R) {
int fl=1;
rep(j,0,c-1) if((S2&(1<<j)) && (S2&(1<<(j+1)))) fl=0;
if(!fl) {
if(!S2) break;
continue;
}
(dp[t][S2]+=dp[t-1][S1])%=P;
if(!S2) break;
}
}
}
ll res=0;
rep(i,0,A[t]) res+=dp[t][i];
res%=P;
return res;
}
int main(){
n=rd();
ll ans=1;
rep(i,1,n) {
if(i%2==0||i%3==0) continue;
//这是一个联通块中最左上角的点
ans=ans*Solve(i)%P;
}
printf("%lld\n",ans);
}