进阶训练赛(四)题解

A.交换a,b的值

直接输出b,a

void solve()
{
    int a,b;
    cin>>a>>b;
    cout<<b<<" "<<a;
 
}

B.素数回文数的个数

从11~n遍历一遍找出满足即是回文数又是素数的数

bool isPalindrome(int n)//判断是否是回文数
{
    string s = to_string(n);
    int i=0,j = s.size()-1;
    while(i<j){
        if(s[i++]!=s[j--]) return false;
    }
    return true;
}

bool isPrime(int n)//判断是否是素数
{
    if(n==1) return false;
    for(int i=2;i<=n/i;i++){
        if(n%i==0) return false;
    }
    return true;
}

void solve()
{
    int n;
    cin>>n;
    int res = 0;
    for(int i=11;i<=n;i++){
        if(isPalindrome(i) && isPrime(i)) res++;
    }
    cout<<res;
}

C.机器翻译

利用队列实现,用一个vis数组记录内存中是否有某个单词,如果在不在内存内,查询次数+1,队首元素删除,将刚查询的元素入队,更新vis数组

bool vis[1000+10];//记录内存单词状态
void solve()
{
    int n,m;
    cin>>n>>m;  
    queue<int>q;
    int res = 0;//查询次数
    while(m--){
        int e;
        cin>>e;
        if(!vis[e]){
            if(q.size()<n){
                q.push(e);
            }else{
                vis[q.front()] = false;
                q.pop();
                q.push(e);
            }
            res++;
            vis[e] = true;
        }
    }
    cout<<res;
}

 D.素数对

先用素数筛将1~n的素数预处理出来,再遍历一遍预处理出来的素数表prime,相邻两个素数若相差2则输出

int prime[10000+10];
bool vis[10000+10];
int cnt;

void get_prime(int n)//筛质数
{
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            prime[cnt++] = i;
            for(int j=2*i;j<=n;j+=i) vis[j] = true;
        }
    }
}

void solve()
{
    int n;
    cin>>n;
    get_prime(n);
    int res = 0;//记录素数对的个数
    for(int i=0;i<cnt-1;i++){
        if(prime[i+1]-prime[i]==2){
            res++;
            cout<<prime[i]<<" "<<prime[i+1]<<endl;
        }
    }
    if(res==0) cout<<"empty"<<endl;

}

E.[蓝桥杯2022初赛] 特殊时间

直接纸上模拟比较方便。首先先从0~9中十个数字选则两个不同的数字,月份中至少包含一个0或一个1,所以选数字时至少选一个0或1。可以选择的数对为(0,1),(0,2)...(0,9),(1,2),(1,3)...(1,9)。

(0,1):3个1的情况:4*4*4 = 64(年份可排列数量*月份日期可排列数量*时间可排列数量)

                3个0不满足月份日期

(0,2):3个2的情况 4*1*4 = 16

(0,3):3个3+1个0无法排列成正常月份日期

(0,4)~(0,9)情况均为0

(1,2): 3个1:4*3*4 = 48

              3个2:4*1*4 = 16

(1,3):3个1:4*1*3 = 12(11月份最多到30日)

              3个3开始情况均为0

(1,4):3个1:4*1*3 = 12

(1,5):3个1:4*1*3 = 12

(1,6)~(1,9):4*1*2 = 8

64+16+48+16+12*3+8*4 = 212

void solve()
{
    cout<<212;
}

F.[蓝桥杯2022初赛] 求和

利用前缀和数组计算

a[1]·a[2 ]+a[1]·a[3]+...+a[1]·a[n]转化为a[i]*(a[2]+a[3]+....a[n])

(a[2]+a[3]+....a[n])可以通过前缀和数组s[n]-s[1]得出

后面的项同理

#define int long long
int a[200000+10];//原始数组
int s[200000+10];//前缀和数组
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        s[i] = s[i-1]+a[i];
    }
    int res = 0;
    for(int i=1;i<=n-1;i++){
        res += (s[n]-s[i])*a[i];
    }
    cout<<res;
}

 G: 剪切

长方形对半分面积为w*h/2

如果(x,y)在长方形对角线交点,则不止一种分割方法,否则,只有一种分割方法

void solve()
{
    double w,h,x,y;
    cin>>w>>h>>x>>y;
    printf("%.6lf ",w*h/2);
    if(abs(2*x-w)<1e-7 && abs(2*y-h)<1e-7) cout<<1;
    else cout<<0;

}

H: 孙权点兵,多多乱点

遍历第2~n-1个,如果a[i]为a[i-1],a[i],a[i+1]三个数的中间数,结果数+1

void solve()
{
    int n;
    cin>>n;
    int a[n+10];
    int res = 0;
    fer(i,1,n) cin>>a[i];
    fer(i,2,n-1){ //for(int i=2;i<=n-1;i++)
        if( (a[i]<a[i-1] && a[i]>a[i+1]) || (a[i]>a[i-1] && a[i]<a[i+1]) ) res++;
    }
    cout<<res;
}

I: 许攸说

如果序列长度是奇数,无法将区间分成两个长度相等的子序列

如果序列长度是偶数,数据量为1e5,可以进行排序,排序后,从a[n/2]到a[n/2+1]的数字均为符合条件的耐力值k

int a[100000+10];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    sort(a+1,a+n+1);
    if(n&1) cout<<0<<endl;
    else{
        cout<<a[n/2+1]-a[n/2]<<endl;
    }
}

J: 借钱

数据量只有20,可以直接暴力查找对比,如果距离为整数,sqrt(dist)和(int)(sqrt(dist))会相等,否则不等

void solve()
{
    int n,d;
    cin>>n>>d;
    int res = 0;
    int a[n+10][d+10];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=d;j++) cin>>a[i][j];
    }
    int i,j,k;
    for(i=1;i<=n;i++){
        for(j=i+1;j<=n;j++){
            double dist=0;
            for(k=1;k<=d;k++){
                dist += (a[i][k]-a[j][k])*(a[i][k]-a[j][k]);
            }
            //cout<<sqrt(dist)<<" "<<(int)(sqrt(dist))<<endl;
            if(sqrt(dist)==(int)(sqrt(dist))){
                res++;
            }
        }
    }
    cout<<res;
}

K.姜维摆七星灯

题意为相邻元素乘积 <= N的序列数

用dp做,推出状态转移方程:

dp[i][j] = \sum_{x*j<=N}dp[i-1][x]

数据是1e9级别的,暴力会TLE,而N/j的取值只有2*\sqrt{N}种情况,用整除分块的方法把答案相同的dp[i][j]合在一起,然后乘起来转移

OiWiki上有整除分块相关知识:https://oi-wiki.org/math/number-theory/sqrt-decomposition/

typedef long long ll;
#define PII pair <ll,ll>
const ll mod=1e9+7;
const int N = 100000+10;
ll n,m,k;
map<ll,ll> mp;
PII g[N+10];
ll dp[100][N+10];

void solve()
{
    cin>>n>>k;
    for(ll l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        g[++m]={r,r-l+1};
        mp[r] = m;
    }
    for(int i=1;i<=m;i++){
        dp[1][i]=(dp[1][i-1]+g[i].second)%mod;
    }
    for(int i=2;i<=k;i++){
        for(int j=1;j<=m;j++){
            dp[i][j]=(dp[i][j-1]+g[j].second*dp[i-1][mp[n/g[j].first]]%mod)%mod;
        }
    }
    cout<<dp[k][m];
}

L.汉寿亭侯

这个问题关键是两点:一个是将问题转换成小球放盒子(可重复),另一个是求组合数C(a,b)的算法。求组合数有多种方法,这里我用的是卢卡斯定理的模板,lucas(a,b)\equivC(a,b)

设要将宝石分成t堆(1<=t<=k),将问题转化一下

1.先进行分组,利用插板法把k个小球分成t组

2.然后插入到n-k+1个间隔当中去

答案最终为:

#define mod 1000000007
#define int long long
int qmi(int a, int k, int p)
{
    int res = 1 % p;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}
 
int C(int a, int b, int p)  // 求组合数C(a, b)
{
    if (a < b) return 0;
    LL x = 1, y = 1;  // x是分子,y是分母
    for (int i = a, j = 1; j <= b; i --, j ++ )
    {
        x = (LL)x * i % p;
        y = (LL) y * j % p;
    }
    return x * (LL)qmi(y, p - 2, p) % p;
}
 
int lucas(LL a, LL b, int p)
{
    if (a < p && b < p) return C(a, b, p);
    return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
 
void solve()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=k;i++){
      int ans = lucas(n-k+1,i,mod)*lucas(k-1,i-1,mod)%mod;
      cout<<ans<<endl;
    }
 
}

M.赵子龙七进七出

用bfs处理最小步数,邻接矩阵存储图,搜索的时候三次跳跃才算一步,所以需要开到二维数组来记录

queue<PII>q;
int vis[100000+10][3];
int dp[100000+10][3];
vector<int>g[100000+10];
int s,t;
int n,m;

void bfs(){
    dp[s][0] = 0;
    vis[s][0] = 1;
    q.push({s,0});
    while(!q.empty()){
        auto t = q.front();
        q.pop();
        int tx = t.first;
        int ty = t.second;
        int y = (ty+1)%3;
        for(auto it:g[tx]){
            if(!vis[it][y]){
                dp[it][y] = dp[tx][ty]+1;
                q.push({it,y});
                vis[it][y] = 1;
            }
        }
    }
}

void solve()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
    }
    cin>>s>>t;
    bfs();
    if(!vis[t][0]) cout<<-1<<endl;
    else cout<<dp[t][0]/3<<endl;
}

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值