A-A+B Problem

https://ac.nowcoder.com/acm/contest/120561/A

题意:小苯有 8 个故障的七段数码管(每段亮灯概率为 pi%),需完成: 把 8 个数码管分成两组各 4 个,分别组成四位数 A、B; 要求: 每个数码管至少亮一段; 每个数码管显示合法数字(0-9); A+B=C(C 是指定数,0≤C≤2026)。 求满足所有条件的概率(结果用数模表示)。

这题整个思路不难就是先算出0~9的概率,再直接暴力算出能满足的A和B的数字,把这个数字拆分成四位,然后算A和B的概率,再把所有可能相加。难点就是要把pi百分数转换成真实的概率再%mod,就需要用逆元(p/100)%mod=(p*100的mod-2次幂)%mod,计算幂值就用快速幂

using namespace std;
//#define int long long
const long long mod=998244353;
#define endl '\n'
long long ksm(long long x,long long y)//快速幂
{
    long long ans=1;
    while(y)
    {
        if(y&1)
        {
            ans=ans*x%mod;
        }
        x=x*x%mod;
        y>>=1;
    }
    return ans;
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    for(int i=0;i<t;i++)
    {
        int c;
        cin>>c;
        vector<long long>p(8);
        for(int j=1;j<=7;j++)
        {
            cin>>p[j];
        }
        for(int j=1;j<=7;j++)
        {
            p[j]=p[j]*ksm(100,mod-2)%mod;
        }
        vector<long long>pi(10);
        pi[0]=p[1]*p[2]%mod;
        pi[0]=pi[0]*p[3]%mod;
        pi[0]=pi[0]*p[5]%mod;
        pi[0]=pi[0]*p[7]%mod;
        pi[0]=pi[0]*p[6]%mod;
        pi[0] = pi[0] * ((1 - p[4] + mod) % mod) % mod;//计算0~9出现概率,分布算保证不溢出
        
        pi[1]=p[3]*p[6]%mod;
        pi[1]=pi[1]*((1-p[1]+mod)%mod)%mod;
        pi[1]=pi[1]*((1-p[2]+mod)%mod)%mod;
        pi[1]=pi[1]*((1-p[4]+mod)%mod)%mod;
        pi[1]=pi[1]*((1-p[5]+mod)%mod)%mod;
        pi[1]=pi[1]*((1-p[7]+mod)%mod)%mod;
        
        pi[2] = p[1] * ((1 - p[2] + mod) % mod) % mod;
        pi[2] = pi[2] * p[3] % mod;
        pi[2] = pi[2] * p[4] % mod;
        pi[2] = pi[2] * p[5] % mod;
        pi[2] = pi[2] * ((1 - p[6] + mod) % mod) % mod;
        pi[2] = pi[2] * p[7] % mod;
        
        pi[3] = p[1] * ((1 - p[2] + mod) % mod) % mod;
        pi[3] = pi[3] * p[3] % mod;
        pi[3] = pi[3] * p[4] % mod;
        pi[3] = pi[3] * ((1 - p[5] + mod) % mod) % mod;
        pi[3] = pi[3] * p[6] % mod;
        pi[3] = pi[3] * p[7] % mod;
        
        pi[4] = ((1 - p[1] + mod) % mod) * p[2] % mod;
        pi[4] = pi[4] * p[3] % mod;
        pi[4] = pi[4] * p[4] % mod;
        pi[4] = pi[4] * ((1 - p[5] + mod) % mod) % mod;
        pi[4] = pi[4] * p[6] % mod;
        pi[4] = pi[4] * ((1 - p[7] + mod) % mod) % mod;
        
        pi[5] = p[1] * p[2] % mod;
        pi[5] = pi[5] * ((1 - p[3] + mod) % mod) % mod;
        pi[5] = pi[5] * p[4] % mod;
        pi[5] = pi[5] * ((1 - p[5] + mod) % mod) % mod;
        pi[5] = pi[5] * p[6] % mod;
        pi[5] = pi[5] * p[7] % mod;
        
        pi[6] = p[1] * p[2] % mod;
        pi[6] = pi[6] * ((1 - p[3] + mod) % mod) % mod;
        pi[6] = pi[6] * p[4] % mod;
        pi[6] = pi[6] * p[5] % mod;
        pi[6] = pi[6] * p[6] % mod;
        pi[6] = pi[6] * p[7] % mod;
        
        pi[7] = p[1] * ((1 - p[2] + mod) % mod) % mod;
        pi[7] = pi[7] * p[3] % mod;
        pi[7] = pi[7] * ((1 - p[4] + mod) % mod) % mod;
        pi[7] = pi[7] * ((1 - p[5] + mod) % mod) % mod;
        pi[7] = pi[7] * p[6] % mod;
        pi[7] = pi[7] * ((1 - p[7] + mod) % mod) % mod;
        
        pi[8] = p[1] * p[2] % mod;
        pi[8] = pi[8] * p[3] % mod;
        pi[8] = pi[8] * p[4] % mod;
        pi[8] = pi[8] * p[5] % mod;
        pi[8] = pi[8] * p[6] % mod;
        pi[8] = pi[8] * p[7] % mod;
        
        pi[9] = p[1] * p[2] % mod;
        pi[9] = pi[9] * p[3] % mod;
        pi[9] = pi[9] * p[4] % mod;
        pi[9] = pi[9] * ((1 - p[5] + mod) % mod) % mod;
        pi[9] = pi[9] * p[6] % mod;
        pi[9] = pi[9] * p[7] % mod;

        long long ans=0;
        for(int j=0;j<=c;j++)//枚举可能取值
        {
            int g=j%10;//拆分成四位
            int s=j/10%10;
            int b=j/100%10;
            int q=j/1000;
            int m=c-j;
            if(m < 0) continue; 
            int g1=m%10;
            int s1=m/10%10;
            int b1=m/100%10;
            int q1=m/1000;
            long long probA = pi[q] * pi[b] % mod;//计算数A对应的概率
            probA = probA * pi[s] % mod;
            probA = probA * pi[g] % mod;
            long long probB = pi[q1] * pi[b1] % mod;//算B对应概率
            probB = probB * pi[s1] % mod;
            probB = probB * pi[g1] % mod;
            ans=(ans+probA*probB%mod)%mod;//A和B概率相乘再把所有可能情况相加
        }
        cout<<ans<<endl;
    }
    return 0;
}

代码写的有点复杂,分布保证不溢出

G-Digital Folding

https://ac.nowcoder.com/acm/contest/120561/G

题意:就是给定L,R区间,让你求这个区间里面的数翻转后最大 要求翻转的数最大,就要尽量让低位是9并且挨近R,思路是L如果和R位数相同,那就直接比较他们第一位不同的位置,将这个变为R上的数字减1,后面的变为9,前面的数照抄;如果位数不同,那就直接缩小范围(比如让L变为与R位数相同的最小值再比较),特殊要判断的是L等于R就直接翻转

using namespace std;
#define endl '\n'
string re(string s)
{
    int x=0;
    while(x<s.size()&&s[x]=='0')
    {
        x++;
    }
    return x==s.size() ? "0" : s.substr(x);
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        string L, R;
        cin >> L >> R;
        int n = R.size();
        
        // 特殊处理 R 是 1000...0 的情况
        string tr(n, '0');
        tr[0] = '1';
        if(R == tr)
        {
            if(L == R)//如果L和R都是100..0时,翻转为1
            {
                cout << 1 << endl;
            }
            else
            {
                cout << string(n-1, '9') << endl;//不是直接r-1
            }
            continue;
        }
        
        //保证 L 和 R 位数相同(让构造出来的位数尽可能大),把L变成与R位数相同最小的数,因为L位数比R小,肯定存在
        if(L.size() < R.size())
        {
            L = string(n, '0');
            L[0] = '1';
            //L.back() = '1';
        }
        
        // 找到第一个 L[i] != R[i] 的位置 k
        int k = -1;
        for(int i = 0; i < n; i++)
        {
            if(L[i] != R[i])
            {
                k = i;
                break;
            }
        }
        
        string ans;
        if(k == -1)//L==R相同就直接翻转
        {
            ans = L;
            while(ans.size() > 1 && ans.back() == '0')
            {
                ans.pop_back();
            }
            reverse(ans.begin(), ans.end());
        }
        else//直接构造折叠数
        {
            bool ok = true;
            for(int i = k + 1; i < n; i++)
            {
                ans += '9';//将第一个不同的位置后面的数变为9,如果R后面也都是9,R就是最大的,如果不是就把不同位上的数减1
                ok &= (R[i] == '9');
            }
            ans += (R[k] - !ok);
            for(int i = k - 1; i >= 0; i--)
            {
                ans += L[i];//将前面相同的部分添加到后面
            }
        }
        cout << ans << endl;
    }
    return 0;
}