P2150 [NOI2015] 壽司晚宴

rgw2010發表於2024-08-05

思路:

注意到對於每個數,其 \(>19\) 的質因數最多隻有 \(1\) 個,稱為大質數;對於 \(\le 19\) 的質因數有 \(8\) 個,稱為小質數

設第 \(i\) 個數的小質數集合為 \(h_i\)

那麼考慮對於所有數按照大質數從小到大排序,那麼對於大質數相同的一段,只能放在兩個集合中的一個。

考慮狀態壓縮 \(dp\),定義 \(dp_{S_1,S_2}\) 表示對於取完 \(i\)(可以滾動陣列) 個數後第一/二個集合的小質數集合\(f1_{S_1,S_2}\) 表示這一段的數都放在第一個集合的方案數,\(f2_{S_1,S_2}\) 表示這一段的數都放在第二個集合的方案數。

則狀態轉移方程為(首先要滿足 \(S_1\)\(S_2\) 沒有交集,即 \(S_1 \operatorname{and} S_2 = 0\)):

\[f1_{S_1 \operatorname{or} data_i,S_2} += f1_{S_1,S_2} [S_2 \operatorname{and} h_i = 0] \]

\[f2_{S_1,S_2 \operatorname{or} data_i} += f1_{S_1,S_2} [S_1 \operatorname{and} h_i = 0] \]

\[dp_{S_1,S_2} = f1_{S_1,S_2} + f2_{S_1,S_2} - dp_{S_1,S_2} \]

因為 \(f1\)\(f2\) 都可以不取這一段的數,那麼 \(dp_{S_1,S_2}\) 就被算了兩次,減掉即可。

時間複雜度為 \(O(n2^{16})\)

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=505,M=8,S=(1ll<<M);
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
struct Node{
	ll x;
	ll data;
	bool operator<(const Node&rhs)const{
		return x<rhs.x;
	}
}a[N];
ll n,x,ans,mod;
ll dp[S][S],f1[S][S],f2[S][S];
ll P[]={2,3,5,7,11,13,17,19};
bool End;
int main(){
	n=read(),mod=read();
	for(int i=2;i<=n;i++){
		x=i;
		for(int j=0;j<8;j++){
			if(x%P[j]==0){
				a[i].data|=(1ll<<j);
				while(x%P[j]==0)
				  x/=P[j];
			}
		}
		if(x!=1)
		  a[i].x=x;
	}
	dp[0][0]=1;
	sort(a+2,a+n+1);
	for(int i=2;i<=n;i++){
		if(!a[i].x||a[i].x!=a[i-1].x){
			for(int X=0;X<S;X++)
			  for(int Y=0;Y<S;Y++)
			    f1[X][Y]=f2[X][Y]=dp[X][Y];
		}
		for(int X=S-1;X>=0;X--){
			for(int Y=S-1;Y>=0;Y--){
				if(X&Y)
				  continue;
				if((a[i].data&Y)==0)
				  f1[X|a[i].data][Y]=Add(f1[X|a[i].data][Y],f1[X][Y]);
				if((a[i].data&X)==0)
				  f2[X][Y|a[i].data]=Add(f2[X][Y|a[i].data],f2[X][Y]);
			}
		}
		if(i==n||a[i].x!=a[i+1].x||!a[i].x){
			for(int X=0;X<S;X++)
			  for(int Y=0;Y<S;Y++)
			    dp[X][Y]=(f1[X][Y]+f2[X][Y]-dp[X][Y]+mod)%mod;			
		}
	}
	for(int X=0;X<S;X++)
	  for(int Y=0;Y<S;Y++)
		ans=Add(ans,dp[X][Y]);
	write(ans);
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}

相關文章