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

    洛谷题目链接

    状压$+$构造矩阵

    首先,题目中要求对于每个数$x$,不能选出$2x,3x$,我们可以构造出一个矩阵,如下:

    $egin{matrix}x & 3x & 9x & 27x & ...\2x & 6x & 18x & 54x & ...\4x & 12x & 36x & 108x & ...\8x & 24x & 72x & 216x & ...\... & ... & ... & ... & ...end{matrix}$

    那么显然,当我们取了某个数后,例如取的是$6x$,那么我们就不能取在它右方的$18x$,以及在它下方的$12x$,对于每个矩阵中的数都如此

    而在实际上,我们还要弄出一个边界,例如上面矩阵中的$216x$很可能超过$n$,所以对于每一行,我们都要弄出一个边界,所以所谓的矩阵其实是锯齿状的样子

    限制条件解决了的话,就开始状压吧

    代码:

    #include<iostream>
    #include<cstdio>
    #define int long long
    #define mod 1000000001
    #define N 100007
    using namespace std;
    int n,ans=1;
    int g[21][21],limit[21],vis[N],f[21][1<<12];
    int Get(int x)
    {
    	int maxc=1,maxk=1,chang=x,kuan=x;
    	while(3*chang<=n)
    		++maxk,chang*=3;
    	while(2*kuan<=n)
    		++maxc,kuan*=2;
    	g[1][1]=x;vis[x]=1;
    	for(int i=2;i<=maxk;++i) g[1][i]=g[1][i-1]*3,vis[g[1][i]]=1;
    	for(int i=2;i<=maxc;++i)
    	{
    		g[i][1]=g[i-1][1]*2;
    		vis[g[i][1]]=1;
    		for(int j=2;j<=maxk;++j)
    		{
    			if(g[i][j-1]*3>n)
    				break;			
    			g[i][j]=g[i][j-1]*3;
    			vis[g[i][j]]=1;
    		}
    	}
    	for(int i=1;i<=maxc;++i)
    	{
    		int j; 
    		for(j=1;j<=maxk;++j)
    			if(g[i][j])
    				g[i][j]=0;
    			else
    				break;
    		for(limit[i]=0;j<=maxk;++j)
    			limit[i]|=1<<(maxk-j);
    	}
    	int S=(1<<maxk)-1;
    	for(int i=0;i<=S;++i)
    		if(!(i&(i<<1))&&!(i&(i>>1))&&!(i&limit[1]))
    			f[1][i]=1;
    	for(int i=2;i<=maxc;++i)
    		for(int j=0;j<=S;++j)
    			if(!(j&(j<<1))&&!(j&(j>>1))&&!(j&limit[i]))
    			{
    				f[i][j]=0;
    				for(int o=0;o<=S;++o)
    					if(!(o&(o<<1))&&!(o&(o>>1))&&!(o&limit[i-1])&&!(o&j))
    						f[i][j]=(f[i][j]+f[i-1][o])%mod;//cout<<"sasdasd
    ";
    			}
    				
    	int sum=0;
    	for(int i=0;i<=S;++i)
    		if(!(i&(i<<1))&&!(i&(i>>1))&&!(i&limit[maxc]))
    			sum=(sum+f[maxc][i])%mod;//cout<<sum<<"
    ";
    	
    /*	printf("maxc:%d   maxk:%d
    ",maxc,maxk);
    	for(int i=1;i<=maxc;++i)
    	{
    		printf("%d ",limit[i]);
    	}
    	printf("
    ");
    	for(int i=1;i<=maxc;++i)
    	{
    		for(int j=1;j<=maxk;++j)
    			printf("%d ",g[i][j]);
    		printf("
    ");
    	}*/
    	return sum;
    }
    signed main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)
    		if(!vis[i])
    			ans=(ans*Get(i))%mod;//printf("%d ",ans);
    	printf("%d",ans);
    }
    

      

  • 相关阅读:
    C# 操作DataTable
    SQLSERVER 连接常见问题
    python 3 与python 2连接mongoDB的区别
    图片url 设置大小
    Python在VSCode环境抓取TuShare数据存入MongoDB环境搭建
    excel解决日常问题记录
    安装MAT内存分析工具独立版
    类加载机制介绍
    jvm启动语句
    linux监控系统语句
  • 原文地址:https://www.cnblogs.com/yexinqwq/p/10265462.html
Copyright © 2011-2022 走看看