CodeChef-RNDRATIO Mysterious Ratio 題解
CodeChef-RNDRATIO Mysterious Ratio
題意簡述:
對每個 \(1 \le i \le n\) ,隨機選擇一個數 \(A_i\) ,滿足 \(L_i \le A_i \le R_i\) ,求 \(\mathrm{lcm}_{i=1}^n A_i\) 的期望。
\(1 \le n \le 10^5\) , \(1 \le L_i \le R_i \le 10^5\) 。
Example Input:
2 1 1 3 2 2 4 5 5Example Output:
1 15
好久沒做毒瘤式子題,推錯好幾次...
不過後面用到的乘法差分還是比較新穎,記錄一下吧。
答案顯然是這個式子——
前面的 \(\prod\) 是常數可以先拋開,後面就暴算吧。(等式右邊 \(\cdots\) 後有少許說明)
所以最終的式子便是
其中
顯然 \(f(n)\) 是個積性函數,故可以用線性篩預處理出其前 \(n\) 項,只需用到如下的性質
其實如果所有 \(Li,R_i\) 都相等的話就可以直接上杜教篩 / min25篩之類的黑科技把複雜度優化到 \(O(n^{1-\omega})\) 了,但這道題很不套路的不相等,所以我們需要找到方法對所有 \(T\) 快速計算後面的 \(\prod\) 。
正解巧妙的使用了乘法差分。首先 \(O(n \sqrt n)\) 對所有 \(i\) 都整除分塊一次得到區間,然後在 \(T\) 上做乘法差分,每個分塊區間乘上貢獻 \(S(\lfloor \frac{R_i}{T} \rfloor) - S (\lceil \frac{L_i}{T} \rceil -1)\) 。最後計算的時候掃一遍就能拿到每個 \(T\) 對應的 \(\prod\) 的值了。
注意對 \(0\) 的特殊處理。
CodeChef上跑不過,懶得卡常了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
using namespace std;
typedef long long ll;
typedef double db;
ll Rd(){
ll ans=0;bool fh=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') fh=1; c=getchar();}
while(c>='0'&&c<='9') ans=ans*10+c-'0',c=getchar();
if(fh) ans=-ans;
return ans;
}
ll CD(ll a,ll b){return (a-1)/b+1;} //CeilDiv
const ll MOD=998244353,INF=1E18;
#define _ %MOD
ll PMod(ll x){
if(x>=MOD) return x-MOD;
else if(x<0) return x+MOD;
else return x;
}
ll QPow(ll x,ll up){
PMod(x _);ll ans=1;
while(up){
if(up%2==0) x=x*x _,up/=2;
else ans=ans*x _,up--;
}
return ans;
}
ll Inv(ll x){return QPow(x,MOD-2);}
const ll INV2=Inv(2);
const ll MXN=1E5+5;
ll P[MXN],pN;
bool notP[MXN];
ll f[MXN];//\sum_{d|n} \mu(d) d
void LinearSieve(ll n){
notP[1]=1;for(ll i=2;i<=n;i++) notP[i]=0;
pN=0;f[1]=1;
for(ll i=2;i<=n;i++){
if(!notP[i]){
P[++pN]=i;
f[i]=PMod(1-i);
}
for(ll j=1;j<=pN&&i*P[j]<=n;j++){
notP[i*P[j]]=1;
if(i%P[j]==0){
f[i*P[j]]=f[i];
break;
}
f[i*P[j]]=f[i]*f[P[j]]_;
}
}
}
ll S(ll n){return n*(n+1)_*INV2 _;}
ll N,MI;
ll L[MXN],R[MXN];
/*namespace Normal{
void Solve(){
ll Ans=0;
for(ll t=1;t<=MI;t++){
ll pdt=1;for(ll i=1;i<=N;i++) pdt=pdt*PMod(S(R[i]/t)-S(CD(L[i],t)-1))_;
Ans=(Ans+QPow(t,N-1)*f[t]_*pdt)_;
}
ll pdt=1;for(ll i=1;i<=N;i++) pdt=pdt*Inv(R[i]-L[i]+1)_;
Ans=Ans*pdt _;
printf("%lld\n",Ans);
}
}*/
namespace Lunatic{
ll diff[MXN],zero[MXN];
ll bl[MXN],br[MXN],blN,brN;
void PutDiff(){
for(ll k=1;k<=MI;k++) diff[k]=1,zero[k]=0;
for(ll i=1;i<=N;i++){
brN=0;for(ll t=1;t<=R[i];t=R[i]/(R[i]/t)+1) br[++brN]=R[i]/(R[i]/t);
br[++brN]=MI;
blN=0;for(ll t=MI;t>=1;t=CD(L[i],CD(L[i],t))-1) bl[++blN]=t;
for(ll j=blN,k=1,st=1,ed;st<=MI;st=ed+1){
if(bl[j]<br[k]||k>brN) ed=bl[j--];
else ed=br[k++];
ll t=ed;
ll val=PMod(S(R[i]/t)-S(CD(L[i],t)-1));
if(val){
diff[st]=diff[st]*val _;
diff[ed+1]=diff[ed+1]*Inv(val)_;
}else{
zero[st]++;
zero[ed+1]--;
}
}
}
}
void Solve(){
PutDiff();
ll Ans=0;
ll pdt=1,zn=0;
for(ll t=1;t<=MI;t++){
zn+=zero[t];pdt=pdt*diff[t]_;
if(!zn) Ans=(Ans+QPow(t,N-1)*f[t]_*pdt)_;
}
pdt=1;for(ll i=1;i<=N;i++) pdt=pdt*Inv(R[i]-L[i]+1)_;
Ans=Ans*pdt _;
printf("%lld\n",Ans);
}
}
int main(){
LinearSieve(MXN-1);
ll T=Rd();while(T--){
N=Rd();
MI=INF;
for(ll i=1;i<=N;i++){
L[i]=Rd();R[i]=Rd();
MI=min(MI,R[i]);
}
//Normal::Solve();
Lunatic::Solve();
}
return 0;
}

