[NOI2015][洛谷P2150]寿司晚宴

[NOI2015][洛谷P2150]寿司晚宴

题目描述

为了庆祝NOI的成功开幕,主办方为大家准备了一场寿司晚宴。小G和小W作为参加NOI的选手,也被邀请参加了寿司晚宴。

在晚宴上,主办方为大家提供了n−1种不同的寿司,编号1,2,3,⋯,n-1,其中第种寿司的美味度为i+1(即寿司的美味度为从2到n)。

现在小G和小W希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小G品尝的寿司种类中存在一种美味度为x的寿司,小W品尝的寿司中存在一种美味度为y的寿司,而x与y不互质。

现在小G和小W希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数p取模)。注意一个人可以不吃任何寿司。

数据范围

2<=n<=500,p<=1e9


把一个数从它的质因子角度来考虑,对于x,如果x的质因子均不在集合1中,则可以放入集合2中,以此类推

于是我们得到一个初始的,对于n<=30的状压dp:设dp[i][s1][s2]][s2]为当前选到第i个数,集合1的质因子状态为s1,集合2的质因子状态为s2时的方案数

转移显然,考虑加入第i个数时

\[dp[i][s1|p[i]][s2]+=dp[i-1][s1][s2] (s2\&p[i]==0) \]

\[dp[i][s1][s2|p[i]]+=dp[i-1][s1][s2] (s1\&p[i]==0) \]

其中p[i]表示i的质因子集合

发现这个东西其实可以滚动一下,把第一维数组空间省了,然后s1,s2倒着枚举即可


考虑n<=500的情况,事实上我们并不需要对500以内的质因子都状压,因为一个数至多有1个22以上的质因子,所以只需状压sqrt(N)以内的小因子,对于大因子分开讨论dp进去就行

具体的来说,我们记a[i].first为i的大因子,若i所有的因子都<22,则first=1,记a[i].second为i的质因子集合

在dp的时候 所有大因子相同的数,只能放进一个集合中,所以考虑按照大因子的大小排个序,这样就可以把所有大因子相同的数放在一起算

考虑在dp过程中,仍然记dp[s1][s2][s2]为小因子集合为s1,s2的方案数,对于一段大因子相同的数,我们在进入这段时把dp[]拷贝出来,记作g1,g2,g1[s1][s2]表示这段数都放在集合1里,小因子状态s1,s2时的方案数,g2同理

\[g1[s1|a[i].sec][s2]+=g1[s1][s2](s2\&a[i].sec==0) \]

\[g2[s1][s2|a[i].sec]+=g2[s1][s2](s1\&a[i].sec==0) \]

然后在这一段结尾的时候,把这一段的g1和g2合并回到dp里去

注意到在合并前,dp[s1][s2]表示这一段都没选前,状态为s1,s2时的方案数

然后由于g1,g2在拷贝dp时用的都是dp[s1][s2],所以在g1,g2中都有这个“整段没选前”的方案数,所以合并起来时要减掉,即

\[dp[s1][s2]=g1[s1][s2]+g2[s1][s2]-dp[s1][s2] \]

最后统计答案就

\[ans=\sum_{s1}\sum_{s2}dp[s1][s2] \]

即可。注意取模。看一些大佬存dp时候没开longlong,不知道是不是洛谷题面的锅,题面的模数取值是1e10,实际上好像是1e9

最后上代码

#include<bits/stdc++.h>
using namespace std;
const int prime[8]={2,3,5,7,11,13,17,19};
#define sec second
#define fir first
typedef long long ll;
ll g[4][1<<9][1<<9],dp[1<<9][1<<9];
int bin[30];
pair<int,int> a[510];
int n;
ll p;
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define res(i,x,y) for (int i=x;i>=y;i--)
void prework(){
    bin[0]=1;rep(i,1,20) bin[i]=bin[i-1]<<1;
    rep(i,2,n){
        int x1=i;
        rep(j,0,7){
            if (x1%prime[j]==0) a[i].sec|=bin[j];
            while (x1%prime[j]==0) x1/=prime[j];
        }
        a[i].fir=x1;
    }
    sort(a+2,a+1+n);
}
void add(ll &x,ll y){
    x+=y;
    if (x>=p) x-=p;
}
int main(){
    scanf("%d %lld",&n,&p);
    prework();
    for(int i=2;i<=n;i++) printf("%d\n",a[i].fir);
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    rep(i,2,n){
        if (a[i].fir==1 || a[i].fir!=a[i-1].fir || i==2){
            memcpy(g[1],dp,sizeof(g[1]));
            memcpy(g[2],dp,sizeof(g[2]));
        }
        res(s1,255,0)
            res(s2,255,0){
                if (s1&s2) continue;
                if (!(s2&a[i].sec))add(g[1][s1|a[i].sec][s2],g[1][s1][s2]);
                if (!(s1&a[i].sec))add(g[2][s1][s2|a[i].sec],g[2][s1][s2]);
            }
        if ((i==n) || (a[i].fir!=a[i+1].fir) || (a[i].fir==1)){
            res(s1,255,0)
                res(s2,255,0){
                    if (s1&s2) continue;
                    dp[s1][s2]=g[1][s1][s2]+g[2][s1][s2]-dp[s1][s2];
                    while (dp[s1][s2]>=p) dp[s1][s2]-=p;
                    while (dp[s1][s2]<0) dp[s1][s2]+=p;
                }
        }
    }
    ll ans=0;
    rep(s1,0,255) rep(s2,0,255)
        if (!(s1&s2) && dp[s1][s2]) add(ans,dp[s1][s2]);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-11-01 23:23  CYW_lyr  阅读(164)  评论(0编辑  收藏  举报