【八月刷题打卡】集合选数

75 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

[HNOI2012]集合选数

题目描述

《集合论与图论》这门课程有一道作业题,要求同学们求出 {1,2,3,4,5}\{ 1, 2, 3, 4, 5 \} 的所有满足以下条件的子集:若 xx 在该子集中,则 2x2x3x3x 不能在该子集中。

同学们不喜欢这种具有枚举性质的题目,于是把它变成了以下问题:对于任意一个正整数 n105n \le 10^5,如何求出 {1,2,,n}\{1,2,\ldots ,n\} 的满足上述约束条件的子集的个数(只需输出对 109+110^9+1 取模的结果),现在这个问题就交给你了。

输入格式

只有一行,其中有一个正整数 nn30%30 \% 的数据满足 n20n \le 20

输出格式

仅包含一个正整数,表示 {1,2,,n}\{1,2,\ldots ,n\} 有多少个满足上述约束条件的子集。

样例 #1

样例输入 #1

4

样例输出 #1

8

提示

【样例解释】

88 个集合满足要求,分别是空集,1{1}{1,4}\{1,4\}{2}\{2\}{2,3}\{2,3\}{3}\{3\}{3,4}\{3,4\}{4}\{4\}

【数据范围】

对于 30%30 \% 的数据,n20n \le 20
对于 100%100 \% 的数据,1n1051 \le n \le 10^5

#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
const int maxn=100010;
const int mod=1000000001; 
int n,ans=1,res,last;
int line[20],lim[20];
int a[20][20],f[2][1<<20];
bool check[1<<20],vis[maxn];
inline void build(int x)
{
	for(re int i=1;i<=11;i++)
	{
		if(i==1)a[i][1]=x;
		else a[i][1]=a[i-1][1]*3;
		if(a[i][1]>n)break;
		last=i,line[i]=1,vis[a[i][1]]=1;
		for(re int j=2;j<=18;j++)
		{
			a[i][j]=a[i][j-1]*2;
			if(a[i][j]>n)break;
			line[i]=j;vis[a[i][j]]=1;
		}
		lim[i]=(1<<line[i])-1;
	}
	//printf("case::%d :%d\n",x,last);
}
inline void solve(int x)
{
	res=0;int now=1;
	for(re int i=0;i<=lim[1];i++)f[1][i]=check[i];
	for(re int i=2;i<=last;i++)
	{
		/*for(int j=0;j<=lim[i-1];j++)printf("%d ",f[now^1][j]);
		puts("");*/
		now=i&1;
		for(re int j=0;j<=lim[i];j++)
		{
			f[now][j]=0;
			if(!check[j])continue;
			for(re int k=0;k<=lim[i-1];k++)
			{
				if(check[k]&&!(k&j))f[now][j]=(f[now][j]+f[now^1][k])%mod;	
			}
		}
	}
	//printf("now::%d\n",now);
	//for(int i=0;i<=lim[last];i++)if(check[i])printf("%d\n",i);
	for(re int i=0;i<=lim[last];i++)if(check[i])res=(res+f[now][i])%mod;
	//printf("res::%d\n",res);
}
signed main()
{
	scanf("%lld",&n);
	for(re int i=0;i<(1<<18);i++)check[i]=((i<<1)&i)?0:1;
	for(re int i=1;i<=n;i++)
	{	
		if(vis[i])continue;
		build(i),solve(i);
		if(res)ans=(ans*res)%mod;
	}
	printf("%lld",ans);
	return 0;
}