• [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;
    }
    
  • 相关阅读:
    permission 文档 翻译 运行时权限
    TabLayout ViewPager Fragment 简介 案例 MD
    Log 日志工具类 保存到文件 MD
    OkHttp 官方wiki 翻译 MD
    Okhttp 简介 示例 MD
    OkHttp 官方Wiki之【使用案例】
    DialogPlus
    倒计时 总结 Timer Handler CountDownTimer RxJava MD
    RecyclerView 判断滑到底部 顶部 预加载 更多 分页 MD
    CSS3的媒体查询(Media Queries)与移动设备显示尺寸大全
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/9669681.html
Copyright © 2020-2023  润新知