• 【BZOJ4197】[Noi2015]寿司晚宴 状压DP+分解质因数


    【BZOJ4197】[Noi2015]寿司晚宴

    Description

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

    在晚宴上,主办方为大家提供了 n−1 种不同的寿司,编号 1,2,3,…,n−1,其中第 i 种寿司的美味度为 i+1 (即寿司的美味度为从 2 到 n)。
    现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x 与 y 不互质。
    现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p 取模)。注意一个人可以不吃任何寿司。

    Input

    输入文件的第 1 行包含 2 个正整数 n,p,中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。

    Output

    输出一行包含 1 个整数,表示所求的方案模 p 的结果。

    Sample Input

    3 10000

    Sample Output

    9

    HINT

     2≤n≤500

    0<p≤1000000000

    题解:我们考虑每个素数,它要么在A中,要么在B中,要么都不在。我们定义<sqrt(500)(更具体的说是<=19,因为23*29>500)的质数为小质数,其余的为大质数,则2-n中的任意一个数都可以表示成1(或0)个大质数*若干个小质数。因此我们可以状压小质数,枚举大质数。

    用g[0或1][x][y]表示当前的大质数在A或B中,A的小质数状态为x,B的小质数状态为y的方案数。我们预处理出对于每个大质数,可以和它搭配的 小质数组 有哪些(其实就是枚举一个大质数p的所有倍数,将所有倍数都分解质因数)。如果当前这个数要被A选,相当于A既要选这个大质数也要选这个 小质数组 ,并且B既不能选这个大质数也不能选这个 小质数组 中的任一个质数。我们设这个 小质数组 的状态为S,得到DP方程:

    g[0][x|S][y]=g[0][x|S][y]+g[0][x][y]
    g[1][x][y|S]=g[1][x][y|S]+g[1][x][y]

    我们用f[x][y]统计答案,但是发现存在重复的情况。你可以理解为虽然这个数被A抢走了,使得B不能选这个数,但是A也不想选这个数。或者这个数被B抢走了,但是B也不想选。那么重复的方案数是多少呢?就是之前的f[x][y]。所以新的f[x][y]=g[0][x][y]+g[1][x][y]-旧的f[x][y]。

    同时,对于不包含大质数的数,要特殊处理。对于不同的大质数,用乘法原则统计答案(因为A可以把好多大质数都抢走);对于不同的小质数选择方法,用加法原则统计答案(小质数不能既给A又给B);DP的时候要时刻满足x&y==0。(以上都是只有本蒟蒻才不明白的地方。)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using namespace std;
    int n,mod,ans;
    int f[1<<8][1<<8],g[2][1<<8][1<<8];
    int p2[510];
    int pri[]={2,3,5,7,11,13,17,19};
    vector<int> v[510];
    int getpri(int x)
    {
    	int i,ret=0;
    	for(i=0;i<8;i++)
    	{
    		if(x%pri[i]==0)
    		{
    			ret|=(1<<i);
    			while(x%pri[i]==0)	x/=pri[i];
    		}
    	}
    	v[x].push_back(ret);
    }
    int main()
    {
    	scanf("%d%d",&n,&mod);
    	int i,j,x,y;
    	for(i=2;i<=n;i++)	getpri(i);
    	f[0][0]=1;
    	for(j=0;j<v[1].size();j++)
    	{
    		for(x=(1<<8)-1;~x;x--)
    		{
    			for(y=(1<<8)-1;~y;y--)	if(!(x&y))
    			{
    				if(!(y&v[1][j]))	f[x|v[1][j]][y]=(f[x|v[1][j]][y]+f[x][y])%mod;
    				if(!(x&v[1][j]))	f[x][y|v[1][j]]=(f[x][y|v[1][j]]+f[x][y])%mod;
    			}
    		}
    	}
    	for(i=23;i<=n;i++)
    	{
    		if(!v[i].size())	continue;
    		for(x=0;x<(1<<8);x++)	for(y=0;y<(1<<8);y++)	if(!(x&y))	g[0][x][y]=g[1][x][y]=f[x][y];
    		for(j=0;j<v[i].size();j++)
    		{
    			for(x=(1<<8)-1;~x;x--)
    			{
    				for(y=(1<<8)-1;~y;y--)	if(!(x&y))
    				{
    					if(!(y&v[i][j]))	g[0][x|v[i][j]][y]=(g[0][x|v[i][j]][y]+g[0][x][y])%mod;
    					if(!(x&v[i][j]))	g[1][x][y|v[i][j]]=(g[1][x][y|v[i][j]]+g[1][x][y])%mod;
    				}
    			}
    		}
    		for(x=0;x<(1<<8);x++)	for(y=0;y<(1<<8);y++)	f[x][y]=((g[0][x][y]+g[1][x][y]-f[x][y])%mod+mod)%mod;
    	}
    	for(x=0;x<(1<<8);x++)	for(y=0;y<(1<<8);y++)	ans=(ans+f[x][y])%mod;
    	printf("%d",ans);
    	return 0;
    }
  • 相关阅读:
    案例8高级打砖块游戏
    案例17——Flash地图应用
    案例11——用遮障制作聚光灯效果及冷却动画
    案例1:用Flash CS工具制作进度条
    Flex 中可以应用于 ActionScript 类的元标签
    案例7用Flash Builder做虚拟计算器
    案例16——没特效还玩毛个Flash
    案例9不要在加载时面对苍白的屏幕
    案例15——游戏物品栏的制作(AS3高级应用)
    案例2心跳模拟
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7128413.html
Copyright © 2020-2023  润新知