集合选数 状压DP+图论

别说谁变了你拦得住时间么 提交于 2019-11-28 23:08:22

集合选数


问题:
《集合论与图论》这门课程有一道作业题,要求同学们求出{\(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;  
}  
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!