zoukankan      html  css  js  c++  java
  • [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


    首先说一句:寿司真好吃(逃

    咳,我们回归正题。。。首先看着题就没法状压,(nleqslant 500)完全下不去手。。。然后考虑互质的话,我们有一个套路做法,就是分解质因数。不过500内的质因数也有上百个,还是压不了。。。不过,我们发现一件事情,有很多质因数在每个数里至多只会出现一次!!!

    好,我们缕一下思路,首先,有一些质数会在一个数中出现很多次,他们都(leqslantsqrt{500}),这些质数共计8个。我们对这8个数进行状压,然后剩下的那些质因数,由于它们至多在一个数中出现一次,那么就说明,它们当中有一个被一个人选了,另一个人就不能选这些数了。

    好,我们再次缕一下思路,我们把这些拥有相同大质因数的数放到一堆来,类似于分块的思想。然后记(F[S1][S2])代表小G选的寿司中,前8个质数的状态为S1;小W选的寿司中,前8个质数的状态为S2。然后这样不好在块内转移,我们就再开个(G[2][S1][S2]),每到一个块中,(G[0]=G[1]=F),然后(G[0])代表当前块所代表的大质数被小G选了,(G[1])表示大质数被小W选了。

    然后这个块处理完之后,(F=G[0]+G[1]-F),-F是因为不选的情况被考虑了两次,所以要减掉

    具体的,看代码吧。。。

    /*program from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define inf 0x7f7f7f7f
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')    f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x>=10)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=5e2;
    const int prime[8]={2,3,5,7,11,13,17,19};
    struct S1{
    	int sta,p;
    	bool operator <(const S1 &x)const{return p<x.p;}
    }A[N+10];
    int f[(1<<8)+10][(1<<8)+10];
    int g[2][(1<<8)+10][(1<<8)+10];
    int n,p,All;
    int main(){
    	n=read(),p=read(),All=(1<<8)-1;
    	for (int i=2;i<=n;i++){
    		int x=i;
    		for (int k=0;k<8;k++){
    			if (x%prime[k]==0)	A[i].sta|=1<<k;//记录前8个质数的状态
    			while (x%prime[k]==0)	x/=prime[k];
    		}
    		A[i].p=x;//记录大质数,为1代表没有大质数
    	}
    	sort(A+2,A+1+n);
    	f[0][0]=1;
    	for (int i=2;i<=n;i++){
    		if (i==2||A[i].p!=A[i-1].p||A[i].p==1){//如果是块的开始,或者没有大质数,就令g[0]=g[1]=f
    			memcpy(g[0],f,sizeof(f));
    			memcpy(g[1],f,sizeof(f));
    		}
    		for (int j=All;~j;j--){
    			for (int k=All;~k;k--){
    				if (j&k)	continue;
    				if (!(A[i].sta&k))	g[0][j|A[i].sta][k]=(g[0][j|A[i].sta][k]+g[0][j][k])%p;//小G选了不会和小W有冲突
    				if (!(A[i].sta&j))	g[1][j][k|A[i].sta]=(g[1][j][k|A[i].sta]+g[1][j][k])%p;//和上面同理
    			}
    		}
    		if (i==n||A[i].p!=A[i+1].p||A[i].p==1)//如果是块的结束,或者没有大质数,则令F=G[0]+G[1]-F
    			for (int j=All;~j;j--)
    				for (int k=All;~k;k--)
    					if (!(j&k))
    						f[j][k]=((g[0][j][k]+g[1][j][k]-f[j][k])%p+p)%p;
    	}
    	int Ans=0;
    	for (int j=All;~j;j--)
    		for (int k=All;~k;k--)
    			if (!(j&k))
    				Ans=(Ans+f[j][k])%p;
    	printf("%d
    ",Ans);
    	return 0;
    }
    
  • 相关阅读:
    第二次作业——结对项目之需求分析与原型模型设计
    Git进行代码管理的心得
    安卓开发环境演变
    软件工程的实践项目的自我目标
    若一切能重来
    随堂练习——Rational rose
    第五次软件个人作业
    第三次作业——结对编程
    第二次作业——结对项目之需求分析与原型模型设计
    软工实践练习——使用Git进行代码管理
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/9669681.html
Copyright © 2011-2022 走看看