zoukankan      html  css  js  c++  java
  • [HNOI2012] 集合选数

    题意

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

    分析

    参照hzwer的题解。

    写出如下矩阵

    1 3 9 27…

    2 6 18 54…

    4 12 36 108…

    发现最多有11列。。。

    我们在其中选取一些数,相邻的不能选择

    然后就可以状压求方案数了,但是5没有出现,同样5的倍数也没有出现,7也如此。。

    应该记录哪些数字出现过,没出现过就作为矩阵的第一个元素,最后把若干个矩阵的方案相乘

    时间复杂度

    因为这是按照整除关系构建的三角形图,所以三角形并不高,最坏的情况是第一行是1,最后第k行是(2^k),深度是(O(log n))的,每一行的元素最多也是(O(log n))的,一个元素推到下一行的状态是(O(2^k))的,越深枚举次数越多,但点数也越少,可以考虑对每个三角形图进行分析,一个很难达到的上界时间复杂度是(O(n log n))

    这大概是口胡,不过还是蛮有道理的、

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<algorithm>
    #include<cstring>
    #define rg register
    #define il inline
    #define co const
    template<class T>T read()
    {
    	T data=0;
    	int w=1;
    	char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		data=data*10+ch-'0';
    		ch=getchar();
    	}
    	return data*w;
    }
    template<class T>T read(T&x)
    {
    	return x=read<T>();
    }
    using namespace std;
    typedef long long ll;
    
    co int MAXN=1e5+7,MAXL=20,mod=1e9+1;
    int n;
    int a[MAXL][MAXL];
    int b[MAXL],f[MAXL][1<<MAXL];
    bool mark[MAXN];
    int ans=1;
    
    int add(int x,int y)
    {
    	x+=y;
    	return x>=mod?x-mod:x;
    }
    
    int mul(int x,int y)
    {
    	return (ll)x*y%mod;
    }
    
    int cal(int x)
    {
    	memset(b,0,sizeof b);
    	a[1][1]=x;
    	for(int i=2;i<=18;++i)
    		if(a[i-1][1]*2<=n)
    			a[i][1]=a[i-1][1]*2;
    		else
    			a[i][1]=n+1;
    	for(int i=1;i<=18;++i)
    		for(int j=2;j<=11;++j)	
    			if(a[i][j-1]*3<=n)
    				a[i][j]=a[i][j-1]*3;
    			else
    				a[i][j]=n+1;
    	for(int i=1;i<=18;++i)
    		for(int j=1;j<=11;++j)
    			if(a[i][j]<=n)
    			{
    				b[i]+=(1<<(j-1));
    				mark[a[i][j]]=1;
    			}
    	for(int i=0;i<=18;++i)
    		for(int x=0;x<=b[i];++x)
    			f[i][x]=0;
    	f[0][0]=1;
    	for(int i=0;i<18;++i)
    		for(int x=0;x<=b[i];++x)
    			if(f[i][x])
    				for(int y=0;y<=b[i+1];++y)
    					if((x&y)==0&&(y&(y>>1))==0)
    						f[i+1][y]=add(f[i+1][y],f[i][x]);
    	return f[18][0];
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	read(n);
    	for(int i=1;i<=n;++i)
    		if(!mark[i])
    			ans=mul(ans,cal(i));
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    shell脚本进阶
    sort与uniq命令
    sed命令
    DNS与CDN
    nginx
    Docker Private Registry
    docker存储卷
    docker容器网络配置
    docker容器网络
    docker容器虚拟化
  • 原文地址:https://www.cnblogs.com/autoint/p/9998156.html
Copyright © 2011-2022 走看看