蓝桥杯大赛 ——首场算法团队战题解

1 不同角度

问题描述

在生活中,我们总是根据数值的大小来判断两个数字的大小关系。例如,9999 总是小于 100100,999999 总是小于 10001000。但如果我们换一个角度,将 999999 和 10001000 看成是两个数字字符串,并用字典序来比较它们的大小,那么此时,999999 将大于 10001000。

字典序的比较规则类似于字典中单词的排序:从左到右逐位比较,如果对应位置的字符不同,则较小的字符对应的字符串字典序较小;如果所有对应位置的字符都相同,则比较字符串的长度,较短的字符串字典序较小;如果长度也相同,则两个字符串字典序相同。

现在,给定一个自然数 SS,请你找到一个自然数 TT,使得将 SS、TT 转换为数字字符串后,(数字字符串)TT 的字典序大于(数字字符串)SS,并且在所有满足此条件的数字字符串中,(数字字符串)TT 的字典序是最小的。

输入格式

第一行包含一个整数 t(1≤t≤103)(1t103),表示测试用例的数量。

接下来的 tt 行,每行包含一个不超过 109109 的自然数,表示 SS。

输出格式

对于每个测试用例,输出一个整数,表示满足条件的自然数 TT。

样例输入

2
99
999

样例输出

990
9990

贪心题。普通的数字在后面加0是大于并且字典序最小的,但是如果输入的是0,1才是最小的。
#include <iostream>
using namespace std;
int main()
{
    string s;
    int t;
    for(cin>>t;t;t--)
    {
        cin>>s;
        if(s=="0")
            cout<<"1\n";
        else
            cout<<s<<0<<endl;
    }
}
1 不同角度

6 看不清的数字 

问题描述

今天是 1024 程序员节,大家都开开心心地去参加节日活动了,只有小蓝还在苦苦加班 😭。

起因是领导在小蓝的工位上贴了三张便利贴,上面写着三个范围在 11 到 NN 之间的正整数。可这三个数被一些同事涂鸦得乱七八糟,导致无法看清这三个数的确切数值,只知道:

  1. 三个数互不相同。

  2. 每个数的各个数位上都不包含数字 11、22 和 44。

  3. 三个数的和除以 10241024 的余数等于 MM。

于是,领导便要求小蓝加班找到所有满足上述条件的三个数,只有这样,小蓝才可以下班。

为了能尽快下班,享受这难得的节日,小蓝找到了你。现在,就请你帮助小蓝算算,总共有多少种满足条件的组合(如果通过改变三个正整数的顺序可以得到相同的组合,则这样的组合也被视为同一种。例如,对于 N=10,M=15N=10,M=15,无论是 (3,5,7)(3,5,7) 还是 (5,3,7)(5,3,7),都只算作一种)。

由于可能的组合数量可能非常巨大,你只需要输出答案对 109+7109+7 取余后的结果即可。

输入格式

第一行包含两个整数 NN 和 MM(3≤N≤10163N1016,0≤M≤10230M1023),其含义如题所述。

输出格式

输出仅一行,包含一个整数,表示满足条件的组合数量对 109+7109+7 取余后的结果。

样例输入

10 15
样例输出
1

样例说明

满足条件的组合仅有一组:(3,5,7)(3,5,7)。

首先考虑计算cnt[x]表示1~n里%1024=x的数字数量,然后用cnt进行计数。

为了计算cnt[x],我们进行数位dp:f[i][j][k]表示在从高往低i位(i),是否卡着上界(j),%1024值为k的数字数量。转移时枚举第i位,这一位使用了数字j,前一位的%1024的值即可进行转移。

有了cnt之后,方案要么是i j k三个%1024各不相同的数;要么是i i k这种两个相同,一个不同的数,要么是i i i这种三个相同的数字。O(n^2)地枚举i j即可计算。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[100];
ll len,f[100][2][2000],mod=1e9+7,cnt[2000],ans,m;
ll ksm(ll a,ll b)
{
    ll res=1;
    while(b){if(b&1)res=res*a%mod;a=a*a%mod;b>>=1;}
    return res;
}
int main()
{
    scanf("%s",s);
    cin>>m;
    m=m%1024;
    len=strlen(s);
    for(int i=1;i<=9&&i<s[0]-'0';i++)
    {
        if(i==1||i==2||i==4)
            continue;
        f[0][0][i]=1;
    }
    if(s[0]!='1'&&s[0]!='2'&&s[0]!='4')
        f[0][1][s[0]-'0']=1;
    for(int i=1;i<len;i++)
    {
        for(int j=0;j<=9;j++)
        {
            if(j==1||j==2||j==4)
                continue;
            if(j!=0)
                f[i][0][j]=(f[i][0][j]+1)%mod;
            for(int l=0;l<1024;l++)
            {
                f[i][0][(l*10+j)%1024]=(f[i][0][(l*10+j)%1024]+f[i-1][0][l])%mod;
                if(j<s[i]-'0')
                    f[i][0][(l*10+j)%1024]=(f[i][0][(l*10+j)%1024]+f[i-1][1][l])%mod;
            }   

        }
        if(s[i]!='1'&&s[i]!='2'&&s[i]!='4')
            for(int l=0;l<1024;l++)
                f[i][1][(l*10+s[i]-'0')%1024]=(f[i][1][(l*10+s[i]-'0')%1024]+f[i-1][1][l])%mod;
    }
    for(int i=0;i<1024;i++)
        cnt[i]=(f[len-1][1][i]+f[len-1][0][i])%mod;
    for(int i=0;i<1024;i++)
    {
        for(int j=0;j<1024;j++)
        {
            int k=((m-i-j)%1024+1024)%1024;
            if(i!=j&&j!=k&&i!=k)
                ans=(ans+cnt[i]*cnt[j]%mod*cnt[k]%mod*ksm(6,mod-2)%mod )%mod;
        }
        int k=((m-i-i)%1024+1024)%1024;
        if(i!=k)
            ans=(ans+cnt[i]%mod*(cnt[i]-1)%mod*cnt[k]%mod*ksm(2,mod-2)%mod )%mod;
        else
            ans=(ans+cnt[i]*(cnt[i]-1)%mod*(cnt[i]-2)%mod*ksm(6,mod-2)%mod )%mod;
    }
    cout<<(ans+mod)%mod;
}
6 看不清的数字

 7 三个公式

问题描述

给定一个长度为 NN 的 0101 数列 A1,A2,⋯,ANA1,A2,,AN 和一个长度为 NN 的 0101 数列 B1,B2,⋯,BNB1,B2,,BN

对于一个整数对 x,yx,y(x≤yxy),定义公式 F(x,y)F(x,y)、G(x,y)G(x,y)、H(x,y)H(x,y) 分别为:F(x,y)=Ax+Ax+1+⋯+AyG(x,y)=Ax∨Ax+1∨⋯∨AyH(x,y)=Bx⊕Bx+1⊕⋯ByF(x,y)=Ax+Ax+1++AyG(x,y)=AxAx+1AyH(x,y)=BxBx+1By其中,∨ 和 ⊕ 分别表示逻辑或、异或运算。

现在,对于所有满足以下两条件的整数对 l,rl,r,请你求出 r−l+1rl+1 最大值:

  1. 1≤l≤r≤N1lrN
  2. F(l,r)−G(l,r)=H(l,r)F(l,r)G(l,r)=H(l,r)

输入格式

第一行包含一个整数 NN(1≤N≤1051N105),表示数列的长度。

第二行包含 NN 个 00 或 11 的整数,表示数列 AA。

第三行包含 NN 个 00 或 11 的整数,表示数列 BB。

输出格式

输出一个整数,表示满足条件的 r−l+1rl+1 的最大值。若不存在这样的 l,rl,r,则输出 00。

样例输入

2
1 0 
0 0
样例输出
2
样例说明

当 l=1,r=2l=1,r=2 时:

  • F(1,2)=1,G(1,2)=1,H(1,2)=0F(1,2)=1,G(1,2)=1,H(1,2)=0,F−G=HFG=H。

此时,r−l+1=2rl+1=2,为最优解。

因为亦或的值只会是0或1,所以F-G=H只有四种情况:0-0=0,1-0=1,1-1=0,2-1=1

但是注意到F和G都是对A数组的,当G为0时F一定也为0,当F为1时G一定也为1,因此可以排除1-0=1的情况,只剩三种情况。

对于2-1=1,考虑对于A数组的1位置10000100010001,左端点和右端点的合法区间如图所示:

 因此扫一遍合法左端点区间,维护pos0和pos1表示B数组前缀异或和为0的第一次出现位置和为1的第一次出现位置。再扫一遍合法右端点区间,更新答案即可。

对于1-1=0,考虑对于A数组的1位置1000010001,左端点和右端点的合法区间如图所示:

 与上类似,扫一遍即可。

对于0-0=0,可以在A数组的连续0内部进行操作。全0数组内部都可以做左右端点。

 

可以类似上文,枚举A数组1位置的中间位置即为连续0的位置。代码中我写了一个双指针来处理。

#include <bits/stdc++.h>
using namespace std;
int a[100010],b[100010],ans,n;
vector<int>pos;
int main()
{
    cin>>n;
    pos.push_back(0);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(a[i]==1)
            pos.push_back(i);
    }
    pos.push_back(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>b[i];
        b[i]=b[i-1]^b[i];
    }
    for(int i=1;i+2<pos.size();i++)//本循环只处理2-1=1的情况
    {
        int l=pos[i-1]+1,r=pos[i+2]-1;
        int pos1=-1,pos0=-1;
        for(int x=l;x<=pos[i];x++)
        {
            if(b[x-1]==0&&pos0==-1)
                pos0=x-1;
            if(b[x-1]==1&&pos1==-1)
                pos1=x-1;
        }
        for(int x=pos[i+1];x<=r;x++)
        {
            if(b[x]==1&&pos0!=-1)
                ans=max(ans,x-pos0);
            if(b[x]==0&&pos1!=-1)
                ans=max(ans,x-pos1);
        }
    }
    for(int i=1;i+1<pos.size();i++)//本循环只处理1-1=0的情况
    {
        int l=pos[i-1]+1,r=pos[i+1]-1;
        int pos1=-1,pos0=-1;
        for(int x=l;x<=pos[i];x++){
            if(b[x-1]==0&&pos0==-1)
                pos0=x-1;
            if(b[x-1]==1&&pos1==-1)
                pos1=x-1;
        }
        for(int x=pos[i];x<=r;x++)
        {
            if(b[x]==1&&pos1!=-1)
                ans=max(ans,x-pos1);
            if(b[x]==0&&pos0!=-1)
                ans=max(ans,x-pos0);
        }
    }
    for(int l=1;l<=n;l++)//本循环只处理0-0=0的情况
    {
        if(a[l]!=0)
        continue;
        int r=l;
        while(r<n&&a[r+1]==0)
            r++;
        int pos0=-1,pos1=-1;
        if(b[l-1]==0)
            pos0=l-1;
        else
            pos1=l-1;
        for(int i=l;i<=r;i++)
        {
            if(b[i]==0&&pos0==-1)
                pos0=i;
            if(b[i]==1&&pos1==-1)
                pos1=i;
            if(b[i]==0)
                ans=max(ans,i-pos0);
            else
                ans=max(ans,i-pos1);
        }
        l=r;
    }
    cout<<ans;
}
7 三个公式

 

posted @ 2024-10-25 09:16  zzuqy  阅读(68)  评论(0编辑  收藏  举报