传送门

题意:

给出n,求出 { 1,...,n} { 1 , . . . , n } 的所有满足以 下条件的子集数量:若 x 在该子集中,则 2x 和 3x 不能在该子集中。

n100000 n ≤ 100000

Solution:

没见过类似做法的人见到这道题真的是毫无思路啊…

构造矩阵形如 1248...361224...9183672...2754108216.................. ( 1 3 9 27 . . . 2 6 18 54 . . . 4 12 36 108 . . . 8 24 72 216 . . . . . . . . . . . . . . . . . . )

即每一项的下面是这一项 2 ∗ 2 ,右边是这一项 3 ∗ 3

发现最多是一个 1117 11 ∗ 17 的矩阵,而我们的目标就可以转化求为不选取矩阵中相邻的元素的方案数,这可以用一个简单的状压dp求出来

一个矩阵可能不能包含所有数,所以说建出多个矩阵最后把方案数相乘即可

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int mod=1e9+1;
int n,tot=1,ans,can[1<<11],top;
int f[20][1<<11];
bool vis[100010];
int main()
{
    scanf("%d",&n);
    for (int i=0;i<(1<<11);i++)
    {
        bool p=1;
        for (int j=1;j<11;j++)
            if (((i>>j)&1)&&((i>>j-1)&1)) {p=0;break;}
        if (p) can[++top]=i;
    }
    int nw=1;
    while (nw<=n)
    {
        f[0][1]=1;ans=0;
        int i;int pre=1;
        for (i=1;;i++)
        {
            int pos=nw*(1<<i-1);if (pos>n) break;
            int len=1;vis[pos]=1;
            while (pos*3<=n) vis[pos*3]=1,len++,pos*=3;
            int lim=0;
            while ((can[lim+1]&((1<<len)-1))==can[lim+1]&&lim<top) lim++;
            //for (int j=1;j<=lim;j++) printf("%d %d\n",i,can[j]);
            for (int j=1;j<=pre;j++)
                for (int k=1;k<=lim;k++)
                    if ((can[j]&can[k])==0) f[i][k]=(f[i][k]+f[i-1][j])%mod;
        //  for (int j=1;j<=lim;j++) printf("%d ",f[i][j]);
            pre=lim;
        }
        for (int j=1;j<=top;j++) ans=(ans+f[i-1][j])%mod;
        tot=1ll*tot*ans%mod;
        while (vis[nw]) nw++;
        for (int j=0;j<i;j++)
            for (int k=1;k<=top;k++) f[j][k]=0;
    }
    printf("%d",tot);
}