Luogu P2150 [NOI2015]壽司晚宴
阿新 • • 發佈:2020-07-18
之前各種上課已經聽了好多次了,最近就給它寫掉了
首先考慮一個很naive的DP,首先我們發現題意就是每個質因子只能存在於一個集合中
所以直接把每個質因子是否出現壓入狀態,設\(f_{i,j}\)表示兩個集合中分別是否存在某種質因子
然後因為\(500\)內的質數挺多的,直接就GG了
但是我們考慮到有關因數的常用結論,每個數的質因數最多隻有一個大於等於\(\sqrt n\)
然後我們一算,小於\(\sqrt {500}\)的質數只有\(8\)個,可以用上面的DP來設狀態
然後就涉及到關於大於\(\sqrt n\)的質因子了,顯然我們可以每次列舉它們
因為它們只能放在一個集合中,因此我們直接設\(g1_{i,j},g2_{i,j}\)
顯然\(g1,g2\)的轉移可以從\(f\)推來,最後注意一下空集的情況算重了要減去
#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& #define int long long using namespace std; const int N=505,M=1<<8; struct data { int p,s; // p: greater than sqrt(n)'s prime; s: state of less than sqrt(n)'s prime friend inline bool operator < (const data& A,const data& B) { return A.p<B.p; } }a[N]; int n,mod,f[M][M],g1[N][M],g2[N][M],prime[N],tot,ans; inline bool isprime(CI n) { for (RI i=2;i*i<=n;++i) if (n%i==0) return 0; return 1; } inline void inc(int& x,CI y) { if ((x+=y)>=mod) x-=mod; } signed main() { RI i,j,k,x,y; for (scanf("%lld%lld",&n,&mod),i=2;i<=n;++i) if (isprime(i)) prime[++tot]=i; for (i=2;i<=n;++i) { for (j=1;j<=min(tot,8LL);++j) if (i%prime[j]==0) a[i].s|=(1<<j-1); for (;j<=tot;++j) if (i%prime[j]==0) a[i].p=prime[j]; } for (sort(a+2,a+n+1),f[0][0]=1,i=2;i<=n&&!a[i].p;++i) for (x=M-1;~x;--x) for (y=M-1;~y;--y) if (f[x][y]) { if (!(a[i].s&x)) inc(f[x][y|a[i].s],f[x][y]); if (!(a[i].s&y)) inc(f[x|a[i].s][y],f[x][y]); } for (;i<=n;i=j+1) { for (x=M-1;~x;--x) for (y=M-1;~y;--y) g1[x][y]=g2[x][y]=f[x][y]; for (j=i;j<n&&a[j+1].p==a[j].p;++j); for (k=i;k<=j;++k) for (x=M-1;~x;--x) for (y=M-1;~y;--y) { if (!(a[k].s&x)) inc(g2[x][y|a[k].s],g2[x][y]); if (!(a[k].s&y)) inc(g1[x|a[k].s][y],g1[x][y]); } for (x=M-1;~x;--x) for (y=M-1;~y;--y) f[x][y]=(g1[x][y]+g2[x][y]-f[x][y]+mod)%mod; } for (x=M-1;~x;--x) for (y=M-1;~y;--y) inc(ans,f[x][y]); return printf("%lld",ans),0; }