我們先來觀察f(x)有甚麼梗XD
首先,如果f(x)要大於50,x至少多少呢?
x必須被1、2、3、4、5、...、50整除!
也就是x至少要3099044504245996706400,這應該足夠應付題目範圍(10^17)了吧XD
可想而知x必須非常巨大才能讓f(x)再大一點
因此,我們可以先建表,x至少要多少,才能讓f(x)再增加一點
(以下x代表讓f(v)=y的最小v)
然後,對於一個很巨大的區間,一定有很多個數字的f(v)都是相同的
再來就是計算對於每個y,在[A,B]裡面有多少個v符合f(v)=y,然後數量乘上lolipop(y)再加起來就好了
因此,我們可以將x從大到小枚舉,數數看區間中有幾個可以被x整除且不會被更大的x整除的數字
排除被更大的x整除的數字很簡單,前面數出幾個就扣幾個就好了
為甚麼呢?
假設f(x1)=y1, f(x2)=y2,y1<y2
可以發現,x2一定可以被x1整除
然後就得證了(?)
每回詢問只要枚舉這些不超過50個的y再利用相應的x計算就可以得到答案
時間複雜度少到無法估計(?)
code:
#include<cstdio> #include<queue> #include<map> #include<vector> #include<cassert> using namespace std; typedef long long LL; const LL INF=9223372036854775807LL; struct Pair { LL now,p; Pair(){} Pair(const LL _now,const LL _p):now(_now),p(_p){} bool operator<(const Pair &p)const{return now>p.now;} }; LL A,B; map<LL,LL>DP; void Initialize() { priority_queue<Pair>pq; if(true) { bool *isp=new bool[100]; for(int i=0;i<100;i++)isp[i]=true; for(int i=2;i<100;i++)if(isp[i]) { pq.push(Pair(i,i)); for(int j=2;i*j<100;j++)isp[i*j]=false; } } DP.clear(); DP[1LL]=1; LL lcm=1LL,now=1LL; for(;;) { now=pq.top().now; while(pq.top().now<=now) { const Pair p=pq.top();pq.pop(); if(lcm>INF/p.p)goto finished; lcm*=p.p; if(p.now<=INF/p.p)pq.push(Pair(p.now*p.p,p.p)); } while(lcm%(now+1LL)==0LL)now++; DP[lcm]=now; } finished:; // for(auto it:DP)printf("(%lld,%lld)\n",it.first,it.second); } LL F(const LL x) { if(x==2LL)return 1LL; auto it=DP.upper_bound(x);it--; while(x%(it->first)!=0LL)it--; return (it->second)+1LL; } LL Lolipop(const LL x){return x==1?0LL:Lolipop(F(x))+1;} LL Cnt(const LL interval){return B/interval-(A-1)/interval;} int main() { Initialize(); // for(int i=1;i<=20;i++)printf("%d=%lld\n",i,Lolipop(i)); while(scanf("%lld%lld",&A,&B)==2) { LL used=0LL; LL ans=0LL; for(auto it=DP.rbegin();it!=DP.rend();it++) { const LL cnt=Cnt(it->first); ans+=(cnt-used)*(Lolipop(it->second+1LL)+1LL); used+=cnt-used; } assert(used==B-A+1LL); printf("%lld\n",ans); } return 0; }
沒有留言:
張貼留言
歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原
注意:只有此網誌的成員可以留言。