XXR1T2题解

XXR1T2 题解

题面

题面传送门

原题传送门

真正的原题1

真正的原题2

思路

首先我们有引理:对于一件事情 X,我们每一次做 X 的成功概率是 P,如果成功则不继续做,不成功则继续做,这样做成功的期望次数是 1P

证明:设做成功的期望次数为 num,则有如下式子:

num=1+P×0+(1P)×num

1 代表当前做了 1 次;P×0 代表成功了则不继续做;(1P)×num 代表没成功则继续做,期望次数为 num

移项得 num=1P

那么开始解释这题。

0 只猫的猫舍直接判掉就好了。

每个猫舍独立,所以只要会一个就会全部,假设当前猫舍有 n(n1) 只猫。

首先 n 只猫中取到 1 个没有被抓过的猫的概率为 nn=1,期望次数为 11=1

n 只猫中抓到剩下 n1 只没有被抓过的猫中的一只的概率 P2n1n,期望次数 num21P2=nn1

……

最后 n 只猫抓到剩下唯一一只没有被抓过的猫的概率 Pn1n,期望次数 numn1Pn=n

所以这个猫舍的答案为 ni=1numi=ni=1ni=n×ni=11i

所以只要线性求逆元后加一个前缀和,最后就可以得出每一个猫舍的答案,加起来输出就好了。

于是就可以得到一个 80 分的做法:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll __int128
using namespace std;
const int MN=1e6+5;
const ll mod=1145141999;
ll n,a[MN],inv[MN],sum[MN],ans;
void write(ll n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
int main(){
    freopen("cat.in","r",stdin);
    freopen("cat.out","w",stdout);
    n=read();
    for(int i=1; i<=n; i++) a[i]=read();
    sum[1]=inv[1]=1;for(int i=2; i<MN; i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod,sum[i]=(sum[i-1]+inv[i])%mod;
    for(int i=1; i<=n; i++) ans=(ans+a[i]*sum[a[i]]%mod)%mod;
    write(ans);
    return 0;
}//250216

接下来发现时间有 2s,所以可以有两只 log,于是把加减乘除都用位运算来实现了!

加法

就是在二进制下加,先计算没有进位的 ab,然后将有进位的左移一位,以便后面加,而至于为啥代码中是 (a^b)&b,因为前面 a 已经异或过 b 了。

ll add(ll a, ll b){while(b!=0)a^=b,b=((a^b)&b)<<1;return a%mod;}

减法

减法可以通过加上负数的补码来实现。计算补码可以通过取反加一的方式来实现。

ll jian(ll a, ll b){return add(a,add(~b,1));}

乘法

正常龟速乘,就是和快速幂同样原理。

ll mul(ll a, ll b){
    ll res=0;
    if(a<b)swap(a,b);
    while(b){
        if(b&1)res=add(res,a);
        a<<=1;b>>=1;
    }
    return res%mod;
}

除法

除法可以通过减法和位移来实现。从被除数中减去除数的倍数,直到结果小于除数为止。每次减去的倍数可以通过左移除数来得到。


ll chu(ll a, ll b){
    ll res=0;
    while(a>=b){
        ll tmp=b,x=1;
        while(a>=(tmp<<1)){tmp<<=1;x<<=1;}
        a=jian(a,tmp);res=add(res,x);
    }
    return res;
}

于是,改一下代码就出来了,注意常数。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int MN=1000005;
const ll mod=1145141999;
ll n,a[MN],inv[MN],sum[MN],ans,maxn;
ll add(ll a, ll b){while(b!=0)a^=b,b=((a^b)&b)<<1;return a%mod;}
ll mul(ll a, ll b){ll res=0;if(a<b)swap(a,b);while(b){if(b&1)res=add(res,a);a<<=1;b>>=1;}return res%mod;}
ll jian(ll a, ll b){return add(a,add(~b,1));}
ll chu(ll a, ll b){ll res=0;while(a>=b){ll tmp=b,x=1;while(a>=(tmp<<1)){tmp<<=1;x<<=1;}a=jian(a,tmp);res=add(res,x);}return res;}
int main(){
    freopen("cat.in","r",stdin);
    freopen("cat.out","w",stdout);
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1; i<=n; i=(i&1?add(i,1):i^1)) cin>>a[i],maxn=max(maxn,a[i]);
    sum[1]=inv[1]=1;for(int i=2; i<=maxn; i=(i&1?add(i,1):i^1)) inv[i]=mul(jian(mod,chu(mod,i)),inv[mod%i]),sum[i]=add(sum[jian(i,1)],inv[i]);
    for(int i=1; i<=n; i=(i&1?add(i,1):i^1)) ans=add(ans,mul(a[i],sum[a[i]]));
    cout<<ans;
    return 0;
}
posted @   naroto2022  阅读(42)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
战斗是残酷的,无法做出多余的考虑!