Luogu2150 壽司晚宴
Description
給定 \(n\) 求將 \(2 \to n\) 的所有數分成兩份,使得兩份中的每一對數都互質的方案數,可以有數不在任何一份中
\(n\le 500\)
Solution
不互質就是有共同的因子
那就看兩個人的方案裡面統計因子個數然後狀壓質數嗎?
質數顯然是不能被狀壓的,因為壓不動……
但是一個數必然的構成要不是單獨是一個質數,要不必然有一個 \(\sqrt{500}\) 一下的質因子,目測是隻有$1\to 19\ \ $ \(8\)個素數
啊這裡結論沒有想到位!!!
光想到 \(\sqrt{500}\) 了
Each number could only have one divisor which is greater than \(\sqrt{500}\)
So what we can do is that sorting the number \(2 \to n\) by its greater divisor
It means that if the two num has the same greater divisor, they can' t be chosen by the same person
This is the way how we deal it
嚇死我了,我還以為輸入不了中文了……又能成了就好……
下面的 ”段“ 指那些較大質因子相同的數字組成的那個段,每個素數自成一個段
具體地來說……設一個 \(ans_{i,j}\)
在段開始處理的時候把 \(ans\) 的值賦給 \(f_1,f_2\)
如果 \(S\&T=0\), 那麼這種方案合法
對於段內的每個數字, \(calc\) 一下 它包含的小質數集合
能與上的就加
處理完每個段之後要 \(ans_{S_1,S_2}=f1_{S_1,S_2}+f2_{S_1,S_2}-ans_{S_1,S_2}\)
這裡還得寫滾動陣列
我直接反向處理了,感覺相對好寫一點
最後把所有的合法狀態一統計就完事了
Code
#include<bits/stdc++.h> using namespace std; #define int long long #define For(i,a,b) for(int i=a;i<=b;++i) namespace yspm{ inline int read() { int res=0,f=1; char k; while(!isdigit(k=getchar())) if(k=='-') f=-1; while(isdigit(k)) res=res*10+k-'0',k=getchar(); return res*f; } const int N=510,SZ=1<<8; int n,pri[N],cnt,fl[N],f[SZ][SZ],g[SZ][SZ],ans[SZ][SZ],mod; inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;} inline int del(int x,int y){return x-y<0?x-y+mod:x-y;} struct node{ int id,val,k; #define k(i) num[i].k #define val(i) num[i].val #define id(i) num[i].id bool operator <(const node &a)const{return val<a.val;} }num[N]; inline void calc(int x) { int t=x; id(x)=x; for(int i=1;i<=min(cnt,8ll);++i) { if(x%pri[i]==0) { while(x%pri[i]==0) x/=pri[i]; k(t)|=1<<(i-1); } } if(x>1) val(t)=x; else val(t)=-t; return ; } inline void seive(int x) { For(i,2,x) { if(!fl[i]) pri[++cnt]=i; for(int j=1;j<=cnt&&i*pri[j]<=x;++j) { fl[i*pri[j]]=1; if(i%pri[j]==0) break; } } for(int i=2;i<=x;++i) calc(i); return ; } inline int find(int x) { int res=x; while(val(res+1)==val(res)) ++res; return res; } int s=0,r; inline void work() { For(i,0,s) For(j,0,s) if(!(i&j)) ans[i][j]=del(add(f[i][j],g[i][j]),ans[i][j]); return ; } signed main() { n=read(); mod=read(); seive(n); s=1; for(int i=1;i<=cnt;++i) if(pri[i]<=19) s<<=1; else break; --s; sort(num+2,num+n+1); ans[0][0]=1; for(int i=2;i<=n;++i) { r=find(i); For(j,0,s) For(k,0,s) f[j][k]=ans[j][k],g[j][k]=ans[j][k]; For(j,i,r) { for(int s1=s;s1>=0;--s1) { for(int s2=s;s2>=0;--s2) { if(s1&s2) continue; if((k(j)&s1)==0) f[s1][s2|k(j)]=add(f[s1][s2|k(j)],f[s1][s2]); if((k(j)&s2)==0) g[s1|k(j)][s2]=add(g[s1|k(j)][s2],g[s1][s2]); } } } i=r; work(); } int tot=0; For(i,0,s) For(j,0,s) if(!(i&j)) tot=add(tot,ans[i][j]); printf("%lld\n",tot); return 0; } } signed main(){return yspm::main();}
隨想
唉……最後還是沒想到這題的 \(idea\)
可能到時候還是淪為 \(30pts\) 暴力選手了
積累個經驗吧,按說最近沒少做數論題呀
如果出現有質因子不能被狀態壓縮下來的情況,那麼就按照較大質因子排序,然後每段每段處理
細節上的東西必須要注意,畢竟一個字母讓100->0的事情太常見了