luoguP2150 [NOI2015]寿司晚宴

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int final=255;
int n,mod,pr[15]={2,3,5,7,11,13,17,19};
long long dp[260][260],sub[2][260][260],ans;
struct number{
    int s,prime;
    bool operator<(const number &p)const{return prime<p.prime;}
}num[520];
int main()
{
    cin>>n>>mod;
    for(int i=2;i<=n;i++)
    {
        int x=i;
        for(int j=0;j<8;j++)
        {
            if(x%pr[j])continue;
            num[i].s|=(1<<j);
            while(!(x%pr[j]))x/=pr[j];
        }   
        num[i].prime=x;
    }//预处理所有2-n的质因数,大于sqrt(500)的存入prime,其余状压存入s 
    sort(num+2,num+n+1);//按是否有大于19的质因数分组处理,其余状压 
    dp[0][0]=1;
    for(int i=2;i<=n;i++)
    {
        if(i==2||num[i].prime!=num[i-1].prime||num[i].prime==1)
        {
            memcpy(sub[0],dp,sizeof(dp));//每当遇到不一样的prime时重新统计用sub数组 
            memcpy(sub[1],dp,sizeof(dp));//当prime为1时有状压所以每次都可以统计答案 (为什么AQX不讲.....) 
        }
        for(int s=final;s>=0;s--)for(int t=final;t>=0;t--)if(!(s&t))//逆推类似01背包的原理 
        {
            if(!(num[i].s&t))sub[0][s|num[i].s][t]=(sub[0][s|num[i].s][t]+sub[0][s][t])%mod;//加给第一个人 
            if(!(num[i].s&s))sub[1][s][t|num[i].s]=(sub[1][s][t|num[i].s]+sub[1][s][t])%mod;//加给第二个人 
        }
        if(i==n||num[i].prime!=num[i+1].prime||num[i].prime==1)
        {
            for(int s=0;s<=final;s++)for(int t=0;t<=final;t++)if(!(s&t))
            dp[s][t]=(sub[0][s][t]+sub[1][s][t]-dp[s][t]+mod)%mod;//统计当前prime答案,减去重复计算的dp[s][t] 
        }
    }
    for(int s=0;s<=final;s++)for(int t=0;t<=final;t++)if(!(s&t))ans=(ans+dp[s][t])%mod;
    cout<<ans<<endl;
}

猜你喜欢

转载自www.cnblogs.com/Ace-MYX/p/10364073.html