蓝桥&搜狐畅游 开关问题 费马小定理+快速幂+逆元 +概率

126 阅读2分钟

image.png image.png

思想

一共就会有四种情况:

image.png

我们用a[i]统计每一行开灯的概率,b[i]统计每一行关灯的概率。

c[i]统计每一列开灯的概率,d[i]统计每一列关灯的概率。

sum[i]用来统计所有列开灯的概率,ans[i]用来统计所有列关灯的概率。

因为所有灯泡的默认状态都是关闭状态,所以一个灯泡如果被翻了两次或者没被翻过最终状态都是关闭状态。

如图,同一个位置被1行翻了一次,被列翻了一次就会变为关闭状态:

image.png

所以a[i]要和ans[i]对立,b[i]要和sum[i]对立,这样保证最终状态是开灯状态。

image.png

code

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int N=1e6+10; // 定义一个足够大的数组大小
const int mo=998244353; // 定义模数
int x,y,n,sum,m,k,z,a[N],b[N],c[N],d[N],key; // 定义变量

// 快速幂算法,用于计算 a^b % mo
ll q_pow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%mo; // 如果 b 是奇数,则将 res 乘以 a 并取模
        a=a*a%mo; // 将 a 的平方取模
        b>>=1; // 将 b 右移一位,相当于除以 2
    }
    return res;
}

// 求逆元,用于计算 a 的模逆元,即 a^(-1) % mo
ll inv(ll a){
    return q_pow(a,mo-2); // 根据 Fermat's Little Theorem,a^(mo-2) 即为 a 的模逆元
}

// 主函数
signed main()
{
    int t=1; // 测试用例的数量
    while(t--){
        cin>>n; // 输入网格的行数和列数
        for(int i=1;i<=n;i++)
        {
            cin>>a[i]; 
        }
        for(int i=1;i<=n;i++)
        {
            cin>>x; 
            b[i]=(x-a[i])*inv(x)%mo; // 计算没开灯的概率
            a[i]=a[i]*inv(x)%mo; // 计算开灯的概率
        }
        for(int i=1;i<=n;i++)
        {
            cin>>c[i]; // 输入每列的初始
        }
        int ans=0,sum=0; // 初始化没开灯和开灯的总概率
        for(int i=1;i<=n;i++)
        {
            cin>>x; 
            d[i]=(x-c[i])*inv(x)%mo; // 计算没开灯的概率
            c[i]=c[i]*inv(x)%mo; // 计算开灯的概率
            ans=(ans+d[i])%mo; // 更新没开灯的总概率
            sum=(sum+c[i])%mo; // 更新开灯的总概率
        }
        for(int i=1;i<=n;i++)
        {
            z=(z+a[i]*ans%mo+b[i]*sum%mo)%mo; // 计算最终答案
        }
        cout<<z; // 输出最终答案
    }
    return 0;
}