BZOJ4197 / UOJ129 [Noi2015]壽司晚宴


本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。

 

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!

 

 

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
 

 

正解:狀壓DP+質因數分解

解題報告:

  這道題的思想很巧妙QAQ

  考慮直接算難以考慮,那么我們從題目給定的規則中可以發現其實,選擇了一個數就相當於把這個數的質因子集合選了,因為為了確保第二個人選的和第一個人互質,就不能再選這個質因子。

  考慮一個數最多有一個大於根號$500$的質因子,且可以特殊考慮,而小於等於根號$500$的質因子只有$8$個,所以我們可以使用狀壓$DP$來統計方案。

  因為小於等於根號$500$的質因子可以通過狀壓判掉,但是大於根號$500$的部分我們必須想辦法解決沖突和重復計算的問題。考慮將所有數包含的質因子集合,和大於根號$500$的質因子(如果沒有就是$1$)預處理出來,按大於根號$500$的質因子排序,那么這個質因子相等的區間我們一起處理。

  顯然這相等的一整個區間,必須是只放入第一個人或者只放入第二個人或者都不放入,那么就可以DP了:

  $f[s1][s2]$表示全局第一個人選擇的集合為$s1$,第二個人選擇的集合為$s2$時的方案數,接着把$f$的值賦給$g$,

  $g[0、1][s1][s2]$表示第一個人選擇的集合為$s1$,第二個人選擇的集合為$s2$,同時當前這個大於根號$500$的質因子放入第一個人/第二個人的方案數。

  做一遍$DP$,最后統計完整個相等的區間時,就賦值回$f$:$f[s1][s2]=g[0][s1][s2]+g[1][s1][s2]-f[s1][s2]$,表示的是兩種情況相加,但是因為這個質因子兩個都不放的情況算了兩次,所以需要減掉一次。

  注意$f$、$g$之間相互轉換的時間和條件。

  ps:不含大質因子的時候可以每次都統計一遍答案,因為已經可以用狀態來防止非法情況了,無需特別考慮。

 

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXS = 257;
const int MAXN = 520;
const int end = 256;
int n,prime[12]={2,3,5,7,11,13,17,19};
LL p,f[MAXS][MAXS],g[2][MAXS][MAXS],ans;
//f[s1][s2]表示當前第一個人選的集合為s1,第二個人選的集合為s2的方案數
//g[0][s1][s2],表示對於當前大於根號500質因子相同的一個區間而言的,這個質因子分配給第一個人(或者不分配)的方案數;
//g[1][s1][s2]表示對於第二個人而言的

struct Number{
	int S;//包含質因子的狀態
	int prime;//大於根號500的質因子,沒有則是1
}a[MAXN];

inline bool cmp(Number q,Number qq){ return q.prime<qq.prime; }

inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline void work(){
	n=getint(); scanf("%lld",&p); int x;
	for(int i=2;i<=n;i++) {
		x=i;
		for(int j=0;j<8;j++) {
			if(x%prime[j]>0) continue;
			a[i].S|=(1<<j);
			while(x%prime[j]==0) x/=prime[j];
		}
		a[i].prime=x;
	}
	sort(a+2,a+n+1,cmp); 
	f[0][0]=1;
	for(int i=2;i<=n;i++) {
		if(i==2 || a[i].prime!=a[i-1].prime || a[i].prime==1) {
			memcpy(g[0],f,sizeof(f));
			memcpy(g[1],f,sizeof(f));
		}

		for(int j=end-1;j>=0;j--)
			for(int k=end-1;k>=0;k--) {
				if((j&k)>0) continue;//不合法
				if((a[i].S&k)==0) //不與第二個人沖突,則可以選入第一個人
					g[0][ a[i].S | j ][k]+=g[0][j][k],g[0][ a[i].S | j ][k]%=p;
				if((a[i].S&j)==0) 
					g[1][j][ a[i].S | k ]+=g[1][j][k],g[1][j][ a[i].S | k ]%=p;
			}

		if(i==n || a[i].prime==1 || a[i].prime!=a[i+1].prime) {
			for(int j=end-1;j>=0;j--)
				for(int k=end-1;k>=0;k--) {
					if((j&k)>0) continue;
					/*!!!*/
					f[j][k]=g[0][j][k]+g[1][j][k]-f[j][k];//去掉重復計算沒有選當前這個質因子的情況
				}
		}
	}
	for(int i=end-1;i>=0;i--)
		for(int j=end-1;j>=0;j--)
			if((i&j)==0)
				ans+=f[i][j],ans%=p;
	ans+=p; ans%=p;
	printf("%lld",ans);
}

int main()
{
    work();
    return 0;
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM