集合选数 状压DP+图论

匿名 (未验证) 提交于 2019-12-02 23:56:01

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