[2019.1.10]BZOJ1853 [Scoi2010]幸運數字
首先,我們可以預處理\(r\)以內的幸運數字。
這樣的數字不會很多,設\(r\)內所有幸運數字有\(n\)個,分別是\(a_1,a_2,a_3,...,a_n\)。
然後,我們知道\(m\)以內的\(x\)的倍數有\(\lfloor\frac{m}{x}\rfloor\)個。
那麽答案就是\(\sum_{i=1}^n\lfloor\frac{r}{a_i}\rfloor-\sum_{i=1}^n\lfloor\frac{l-1}{a_i}\rfloor\)?
當然不是,因為會有重復。
所以考慮容斥。
我們設\(f(m,x)=\sum_{i_1=1}^n\sum_{i_2=i_1+1}^n\sum_{i_3=i_2+1}^n...\sum_{i_x=i_{x-1}+1}^n\lfloor\frac{m}{lcm(p_{i_1},p_{i_2},p_{i_3},...,p_{i_n})}\rfloor\)
那麽答案就是\(\sum_{i=1}^n(-1)^{i+1}f(r,i)-\sum_{i=1}^n(-1)^{i+1}f(l-1,i)\)。
可惜這樣做時間復雜度很高,無法承受。
考慮剪枝。
設\(ans(x)=\sum_{i=1}^n(-1)^{i+1}f(x,i)\),那麽問題答案就是\(ans(r)-ans(l-1)\)
看看如何計算\(ans(m)\)。
首先,我們發現當任意數量的幸運數字的\(lcm\)大於\(m\)的時候,就不存在對答案的貢獻了。
所以我們可以減去\(lcm\)已經大於\(m\)的情況。
這樣就省掉了大量無意義運算。
這樣能不能通過此題呢?事實證明不能。
還有以下優化:
1.降序排列\(p\),使得\(lcm\)可以更快地大於\(m\)。
然後你吸個氧氣可能就過了。
如果你還是過不了,我們可以減少\(p\)的長度。
2.發現對於任意\(p_j|p_i\),\(p_i\)的倍數都是\(p_j\)的倍數。
所以刪去\(p_i\)不會影響答案。
然後應該是鐵定過了。
評測記錄(吸了氧氣)
所有優化
只有2
只有1的莫名WA掉了,提交記錄就不放了。
code:
#include<bits/stdc++.h> using namespace std; const int p[15]={0,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1}; long long l,r,n,ln[1050],sz,ued[1050],ans; queue<long long>q; long long gcd(long long x,long long y){ return y?gcd(y,x%y):x; } long long lcm(long long x,long long y){ return x?x*(y/gcd(x,y)):y; } bool P(long long x,long long y){ return ((x<=1e9)||(y<=1e9)); } bool cmp(long long x,long long y){ return x>y; } void Getln(){ long long x; q.push(6),q.push(8); while(!q.empty()){ x=q.front(),q.pop(); for(int i=1;i<=sz;++i)if(x%ln[i]==0)goto END; ln[++sz]=x; END: x*10+6<=r?(q.push(x*10+6),(x*10+8<=r?q.push(x*10+8),0:0)):0; } } void dfs(int x,int tt,long long lm){ (x>sz&&lm)?ans+=p[tt]*(n/lm):0; if(x>sz||lm>n)return; dfs(x+1,tt,lm),P(lm,ln[x])?dfs(x+1,tt+1,lcm(lm,ln[x])),0:0; } long long Work(long long x){ ans=0,n=x,dfs(1,0,0); return ans; } int main(){ scanf("%lld%lld",&l,&r); Getln(); sort(ln+1,ln+sz+1,cmp); printf("%lld",Work(r)-Work(l-1)); return 0; }
[2019.1.10]BZOJ1853 [Scoi2010]幸運數字