CodeChef-RNDRATIO Mysterious Ratio 题解

CodeChef-RNDRATIO Mysterious Ratio

题意简述:

对每个 1in ,随机选择一个数 Ai ,满足 LiAiRi ,求 lcmi=1nAi 的期望。

1n1051LiRi105

Example Input:

2
1
1 3
2
2 4
5 5

Example Output:

1
15

好久没做毒瘤式子题,推错好几次...

不过后面用到的乘法差分还是比较新颖,记录一下吧。

答案显然是这个式子——

i=1n(RiLi+1)1LiAiRi1ini=1nAigcdi=1nAi

前面的 是常数可以先抛开,后面就暴算吧。(等式右边 后有少许说明)

LiAiRiAigcdAi=k=1minRiLiAiRi[gcdAi=k]Aik=kkn1LikBiRik[gcdBi=1]Bi( kBi=Ai )=kkn1LikBiRikd|gcdBiμ(d)Bi( d|nμ(d)=[n=1] )=kkn1d=1minRikμ(d)dnLikdCiRikdCi ( dCi=Bi, abc=abc )=kkn1dμ(d)dni=1nLikdxRikdx=kkn1dμ(d)dni=1n(S(Rikd)S(Likd1))( S(n)=x=1nx )=T=1minRi(d|Tμ(d)(Td)n1dn)i=1n(S(RiT)S(LiT1)) ( T=kd )=TTn1(d|Tμ(d)d)i=1n(S(RiT)S(LiT1))

所以最终的式子便是

i=1n(RiLi+1)1T=1minRiTn1f(T)i=1n(S(RiT)S(LiT1))

其中

S(n)=n(n+1)2

f(n)=d|Tμ(d)d

显然 f(n) 是个积性函数,故可以用线性筛预处理出其前 n 项,只需用到如下的性质

f(pc)=i=0cμ(pi)pi=1p(c2)

其实如果所有 Li,Ri 都相等的话就可以直接上杜教筛 / min25筛之类的黑科技把复杂度优化到 O(n1ω) 了,但这道题很不套路的不相等,所以我们需要找到方法对所有 T 快速计算后面的

正解巧妙的使用了乘法差分。首先 O(nn) 对所有 i 都整除分块一次得到区间,然后在 T 上做乘法差分,每个分块区间乘上贡献 S(RiT)S(LiT1) 。最后计算的时候扫一遍就能拿到每个 T 对应的 的值了。

注意对 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;
}
posted @   sun123zxy  阅读(169)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示