bzoj4197 [Noi2015]寿司晚宴(数学+状压dp)

首先考虑n<=30怎么做,没几个质因子,状压掉
然后n<=100,状压着有点费劲,各种优化,比如*2>n的质因子肯定是贡献3倍答案,*3>n的质因子可以讨论一下也不压进状态里,然后就可以卡过去了
这启发了我们正解的思路!对于大质因子我们一起转移,然后就把这个大质因子这一位扔掉。
我们只需要状压< 500 500 的那么8个质因子就好了。
然后复杂度 O(38n) O ( 3 8 n )

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 510
#define k1 1117
#define eps 1e-8
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline ll read(){
    ll x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,prime[N],tot=0;
ll mod;
bool notprime[N];
inline ll mul(ll x,ll y){ll res=x*y-(ll)((long double)x/mod*y+eps)*mod;res%=mod;return res<0?res+mod:res;}
inline ll ksm(ll x,int k){
    ll res=1;for(;k;k>>=1,x=mul(x,x)) if(k&1) res=mul(res,x);return res;
}
inline void getprime(){
    notprime[1]=1;
    for(int i=2;i<=n;++i){
        if(!notprime[i]) prime[++tot]=i;
        for(int j=1;prime[j]*i<=n;++j){
            notprime[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}
namespace sol1{
    bool used[N];
    struct Hash_table{
        ll val;int key1,key2,next;
    };
    inline void inc(ll &x,ll y){x+=y;x%=mod;}
    struct Icefox{
        Hash_table data[20000];
        int h[k1][k1],num;
        inline void ins(int key1,int key2,ll val){
            int x=key1%k1,y=key2%k1;
            for(int i=h[x][y];i;i=data[i].next)
                if(data[i].key1==key1&&data[i].key2==key2){inc(data[i].val,val);return;}
            data[++num].key1=key1;data[num].key2=key2;data[num].val=val;data[num].next=h[x][y];h[x][y]=num;
        }inline void clear(){
            for(int i=1;i<=num;++i){
                int x=data[i].key1%k1,y=data[i].key2%k1;h[x][y]=0;
            }num=0;
        }
    }f[2];
    int a[N],bin[20];
    inline void gao(){
        bin[0]=1;int m=0,p=0;
        for(int i=1;i<=tot;++i) if(prime[i]*2<=n) ++m;
        for(int i=1;i<=m;++i) bin[i]=bin[i-1]<<1;
        for(int i=2;i<=n;++i)
            for(int j=1;j<=m;++j)
                if(i%prime[j]==0) a[i]|=bin[j-1];
        f[p].ins(0,0,1);
        for(int j=m;j>=1;--j){
            for(int x=prime[j];x<=n;x+=prime[j]){
                if(used[x]) continue;used[x]=1;f[p^1].clear();
                for(int i=1;i<=f[p].num;++i){
                    int s1=f[p].data[i].key1,s2=f[p].data[i].key2;ll val=f[p].data[i].val;
                    if((a[x]&s1)==0) f[p^1].ins(s1,s2|a[x],val);
                    if((a[x]&s2)==0) f[p^1].ins(s1|a[x],s2,val);
                    f[p^1].ins(s1,s2,val);
                }p^=1;
            }
        }ll ans=0;
        for(int i=1;i<=f[p].num;++i) inc(ans,f[p].data[i].val);
        printf("%lld\n",mul(ans,ksm(3,tot-m)));
    }
}
namespace sol2{
    bool used[N],mk[N];
    struct Hash_table{
        ll val;int key1,key2,next;
    };
    inline void inc(ll &x,ll y){x+=y;x%=mod;}
    struct Icefox{
        Hash_table data[180000];
        int h[k1][k1],num;
        inline void ins(int key1,int key2,ll val){
            int x=key1%k1,y=key2%k1;
            for(int i=h[x][y];i;i=data[i].next)
                if(data[i].key1==key1&&data[i].key2==key2){inc(data[i].val,val);return;}
            data[++num].key1=key1;data[num].key2=key2;data[num].val=val;data[num].next=h[x][y];h[x][y]=num;
        }inline void clear(){
            for(int i=1;i<=num;++i){
                int x=data[i].key1%k1,y=data[i].key2%k1;h[x][y]=0;
            }num=0;
        }
    }f[2];
    int a[N],bin[20];
    inline void gao(){
        bin[0]=1;int m=0,p=0,n1=0,n2=0;
        for(int i=1;i<=tot;++i){
            if(prime[i]*3<=n) ++m;
            else if(prime[i]*2<=n) mk[prime[i]*2]=1,++n1;
            else ++n2;
        }
        for(int i=1;i<=m;++i) bin[i]=bin[i-1]<<1;
        for(int i=2;i<=n;++i)
            for(int j=1;j<=m;++j)
                if(i%prime[j]==0) a[i]|=bin[j-1];
        f[p].ins(0,0,1);
        for(int j=m;j>=1;--j){
            for(int x=prime[j];x<=n;x+=prime[j]){
                if(used[x]) continue;used[x]=1;f[p^1].clear();
                for(int i=1;i<=f[p].num;++i){
                    int s1=f[p].data[i].key1,s2=f[p].data[i].key2;ll val=f[p].data[i].val;
                    if((a[x]&s1)==0) f[p^1].ins(s1,s2|a[x],val*(mk[x]?2:1));
                    if((a[x]&s2)==0) f[p^1].ins(s1|a[x],s2,val*(mk[x]?2:1));
                    f[p^1].ins(s1,s2,val*(mk[x]?3:1));
                }p^=1;
            }
        }ll ans=0;
        for(int i=1;i<=f[p].num;++i) inc(ans,f[p].data[i].val);
        printf("%lld\n",mul(ans,ksm(3,n2)));
    }
}
namespace sol3{
    int a[N],bin[10];ll f[2][520][520];
    bool used[N];
    inline void inc(ll &x,ll y){x+=y;x%=mod;}
    inline void gao(){
        bin[0]=1;int m=8,p=0;
        for(int i=1;i<=m+1;++i) bin[i]=bin[i-1]<<1;
        for(int i=2;i<=n;++i)
            for(int j=1;j<=m;++j)
                if(i%prime[j]==0) a[i]|=bin[j-1];
        f[p][0][0]=1;
        for(int j=tot;j>8;--j){
            for(int i=1;;i++){
                if(prime[j]*i>n) break;int x=prime[j]*i;used[x]=1;memset(f[p^1],0,sizeof(f[p^1]));a[x]|=bin[m];
                for(int s1=0;s1<bin[m+1];++s1){
                    int st=bin[m+1]-1-s1;
                    for(int s2=st;s2;s2=(s2-1)&st){
                        if((a[x]&s1)==0) inc(f[p^1][s1][s2|a[x]],f[p][s1][s2]);
                        if((a[x]&s2)==0) inc(f[p^1][s1|a[x]][s2],f[p][s1][s2]);
                        inc(f[p^1][s1][s2],f[p][s1][s2]);
                    }int s2=0;
                    if((a[x]&s1)==0) inc(f[p^1][s1][s2|a[x]],f[p][s1][s2]);
                    if((a[x]&s2)==0) inc(f[p^1][s1|a[x]][s2],f[p][s1][s2]);
                    inc(f[p^1][s1][s2],f[p][s1][s2]);
                }p^=1;
            }for(int s1=0;s1<bin[m];++s1){
                    int st=bin[m]-1-s1;
                    for(int s2=st;s2;s2=(s2-1)&st){
                        inc(f[p][s1][s2],f[p][s1|bin[m]][s2]),f[p][s1|bin[m]][s2]=0;
                        inc(f[p][s1][s2],f[p][s1][s2|bin[m]]),f[p][s1][s2|bin[m]]=0;
                    }int s2=0;
                    inc(f[p][s1][s2],f[p][s1|bin[m]][s2]),f[p][s1|bin[m]][s2]=0;
                    inc(f[p][s1][s2],f[p][s1][s2|bin[m]]),f[p][s1][s2|bin[m]]=0;
            }
        }
        for(int j=1;j<=8;++j){  
            for(int x=prime[j];x<=n;x+=prime[j]){
                if(used[x]) continue;used[x]=1;memset(f[p^1],0,sizeof(f[p^1]));
                for(int s1=0;s1<bin[m];++s1){
                    int st=bin[m]-1-s1;
                    for(int s2=st;s2;s2=(s2-1)&st){
                        if((a[x]&s1)==0) inc(f[p^1][s1][s2|a[x]],f[p][s1][s2]);
                        if((a[x]&s2)==0) inc(f[p^1][s1|a[x]][s2],f[p][s1][s2]);
                        inc(f[p^1][s1][s2],f[p][s1][s2]);
                    }int s2=0;
                    if((a[x]&s1)==0) inc(f[p^1][s1][s2|a[x]],f[p][s1][s2]);
                    if((a[x]&s2)==0) inc(f[p^1][s1|a[x]][s2],f[p][s1][s2]);
                    inc(f[p^1][s1][s2],f[p][s1][s2]);
                }p^=1;
            }
        }ll ans=0;
        for(int s1=0;s1<bin[m];++s1){
            int st=bin[m]-1-s1;
            for(int s2=st;s2;s2=(s2-1)&st) inc(ans,f[p][s1][s2]);inc(ans,f[p][s1][0]);
        }printf("%lld\n",ans);
    }
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();mod=read();getprime();
    if(n<=50) sol1::gao();
    else if(n<=100) sol2::gao();
    else sol3::gao();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值