zoukankan      html  css  js  c++  java
  • [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
    ",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
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Sqlite判断字段存在
    JS实现返回上一页面并刷新(转)
    WebService上传文件到服务器(转)
    ASP.NET中的DES加密解密,可用于URL传参(转)
    my97日期控件网址
    window.open用法详解(转)
    repeater绑定后alter不能弹出的解决办法(转)
    SQL Server之其他函数——类型转换函数(convert用法)(转)
    asp.net 无刷新分页时无法弹出alert对话框的解决方法 (转)
    UVa
  • 原文地址:https://www.cnblogs.com/ugly-CYW-lyr-ddd/p/11780177.html
Copyright © 2011-2022 走看看