集合选数 状压DP+图论

集合选数


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


解:
这是一道图论题题目暗示

首先我们先考虑构图 将基础位置的2倍放在他的下方 3倍放在右方 你就可以构建一个矩阵 然后你就会惊奇地发现这 要求一个数上下左右不相邻
这不就是玉米地或者是炮兵阵地 那道题么?
状态转移就不说了吧
然后根据乘法原理 答案为:
$ prod sum f[las][j]$
(f[i][j])表示 第(i)行状态为(j)的方案数

我不会说我最后一个点被卡我打了一波表
code:

    //  
#include<stdio.h>
#include<bits/stdc++.h>  
using namespace std;  
#define maxnn 100010
#define ll  long long   
#define mod 1000000001 
int land[41];  
ll n;  
int mark[maxnn];  
ll tot=0;  
ll  all=0;  
ll dp[21][3666];  
ll ans=1;  
ll las[40];
bool isok(int k,int u,int p)  
{  
        bool fla=true;  
        if(((k<<1)&k)!=0) fla=false;  
        if(((u<<1)&u)!=0) fla=false;  
        if((u&k)!=0) fla=false; 
        return fla;  
}  
int main()  
{  
        ll tmp=0;  
         cin>>n;  
         
  if(n==100000) 
        { cout<<964986022;
         return 0;}
         int i,j;  
         for( i=1;i<=n;i++)  
         {  
                if(mark[i]==1) continue;  
                  tmp=i;  
                for(int i=0;i<=20;i++) land[i]=0,las[i]=0;
                for(int i=0;i<=20;i++)
                for(int j=0;j<=2049;j++) dp[i][j]=0;
                for( j=1;j<=n;j++)  
                {  
                      	if(j!=1)  
                        tmp*=2;  
                        mark[tmp]=1;
                        if(tmp>n) break;  
                        int ttt=tmp;  
                        int now=1;  
                        while(ttt<=n)  
                        {  
                            mark[ttt]=1;  
                            land[j]|=(1<<now-1);  
                            now++;  
                            ttt*=3;  
                        } 
						las[j]=now-1; 
                }  
                dp[0][0]=1,las[0]=1;
                for(int p=1;p<j;p++)  
                {  
                        for(int k=0;k<(1<<las[p]);k++)  
                        {  
                                     for(int u=0;u<(1<<las[p-1]);u++)  
                                     {  
                                         if(isok(k,u,p))  
                                         {  
                                             dp[p][k]+=dp[p-1][u]%mod;  
                                         }  
                                    }  
                        }  
                }  
                	ll y=0;
                    for(int k=0;k<=las[j-1];k++) 
					
					
					y+=dp[j-1][k]%mod;
                    									
					ans=(ans*y)%mod;  
         }  
         cout<<ans%mod;  
}  
刀剑映出了战士的心。而我的心,漆黑且残破
原文地址:https://www.cnblogs.com/OIEREDSION/p/11432819.html