列表

详情


NC20100. [HNOI2012]集合选数

描述

《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。
同学们不喜欢这种具有枚举性质的题目,于是把它变成了以下问题:对于任意一个正整数 n ≤ 100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。   

输入描述

只有一行,其中有一个正整数 n,30%的数据满足 n ≤ 20。

输出描述

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

示例1

输入:

4

输出:

8

说明:

【样例解释】
有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++11(clang++ 3.9) 解法, 执行用时: 8ms, 内存消耗: 868K, 提交时间: 2019-07-12 10:29:05

#include<bits/stdc++.h>
#define mod 1000000001
#define N 21
using namespace std;
typedef long long ll;
int n;
int a[N][N];
ll f[N][70010];
int vis[100100];
int line[N];
ll calc(int x)
{
    a[1][1]=x;
    int tmp;
    for(int i=1;; i++)
    {
        if(i!=1)
        {
            a[i][1]=a[i-1][1]*2;
            if(a[i][1]>n)
            {
                tmp=i-1;
                break;
            }
        }
        vis[a[i][1]]=1;
        for(int j=2;; j++)
        {
            a[i][j]=a[i][j-1]*3;
            if(a[i][j]>n)
            {
                line[i]=j-1;
                break;
            }
            vis[a[i][j]]=1;
        }
    }
    line[0]=1;
    for(int i=0; i<=tmp; i++)
        for(int j=0; j<(1<<line[i]); j++)
            f[i][j]=0;
    line[tmp+1]=0,f[tmp+1][0]=0;
    f[0][0]=1;
    for(int i=0; i<=tmp; i++)
    {
        for(int j=0; j<(1<<line[i]); j++)
        {
            if(f[i][j])
            {
                if(j&(j>>1))
                    continue;
                for(int k=0; k<(1<<line[i+1]); k++)
                {
                    if(j&k)
                        continue;
                    f[i+1][k]=(f[i+1][k]+f[i][j])%mod;
                }
            }
        }
    }
    return f[tmp+1][0];
}
int main()
{
    scanf("%d",&n);
    ll ans=1;
    for(int i=1; i<=n; i++)
        if(!vis[i])
            ans=ans*calc(i)%mod;
    printf("%lld\n",ans);
}

C++14(g++5.4) 解法, 执行用时: 237ms, 内存消耗: 1144K, 提交时间: 2020-08-19 14:47:07

#include<bits/stdc++.h>
#define Fox ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define turn_on_clock clock_t start,end;start=clock();
#define turn_off_clock end=clock();printf("\nTime eclipse:%.5lfms\n",(double)(end-start)/CLOCKS_PER_SEC*1000);
using namespace std;
const int N=1e5+10;
const int mod=1000000001;
int n,ans=1,v[N];
int num[22],dp[22][4100];
int solve(int x){
	memset(num,0,sizeof(num));
	memset(dp,0,sizeof(dp));
	int cnt=0;
	int tmp=x;
	while(tmp<=n){
		cnt++;
		int c=tmp;
		while(c<=n)num[cnt]++,v[c]=1,c*=3;
		tmp*=2;
	}
	for(int i=0;i<(1<<num[1]);i++)
		if(!((i>>1)&i))dp[0][i]=1;
	int opt=1;
	for(int i=2;i<=cnt;i++,opt=1-opt){
		int Max=(1<<num[i]);
		memset(dp[opt],0,sizeof(dp[opt]));
		for(int j=0;j<Max;j++){
			if((j>>1)&j)continue;
			for(int k=0;k<(1<<num[i-1]);k++)
				if(!(j&k) && !((k>>1)&k))dp[opt][j]=(dp[opt][j]+dp[!opt][k])%mod;
		}
	}
	int rec=0;
	for(int i=0;i<(1<<num[cnt]);i++)
		if(!((i>>1)&i))rec=(rec+dp[!opt][i])%mod;
	return rec;
}
int main(){
	Fox;
	memset(v,0,sizeof(v));
	cin>>n;
	for(int i=1;i<=n;i++)
		if(!v[i])ans=1LL*ans*solve(i)%mod;
	cout<<ans;
	return 0;
}

上一题