2022年ACM暑假集训个人排位赛(1)A-F题解

A: 剩余的数量

题意
现有S和T字符串,其数量分别是A和B
现给你一个字符串U,U=S或者U=T,使其数量-1
问S和T现在的数量是多少
思路
模拟
时间复杂度 O 1 O1 O1

#include<bits/stdc++.h>
using namespace std ;
const int N = 1e6 + 10 ;
string a , b , s ;
int ca , cb ;

int main()
{
    cin >> a >> b >> ca >> cb >> s ;
    if(a == s) ca -- ;
    else cb -- ;
    cout << ca << " " << cb << "\n" ;
    
    return 0 ;
}

B: 替换字符

题意
给你一个字符串,将其中所有字符都替换为x并输出
思路
模拟
时间复杂度 O n On On

#include<bits/stdc++.h>
using namespace std ;
const int N = 1e6 + 10 ;

int main()
{
    string s ;
    cin >> s ;
    for(auto i : s) cout << 'x' ;
    return 0 ;
}

C: 相同或者不同

题意
给你n个数的数组
a 1 a_1 a1 a 2 a_2 a2 a n a_n an
问是否所有数都不一样
是输出YES
否则输出NO
思路
用map记录一下每个数的出现次数
只要有一个数的出现次数大于等于2次
输出NO即可
否则输出YES
时间复杂度 O n l o g n Onlogn Onlogn

#include<bits/stdc++.h>
using namespace std ;
const int N = 1e6 + 10 ;
map<int,int> q ;
int f , n , x ;

int main()
{
    cin >> n ;
    while(n --)
    {
        scanf("%d",&x) ;
        q[x] ++ ;
        if(q[x] >= 2) f = 1 ; 
    }
    puts((f == 1 ? "NO" : "YES")) ;
    
    return 0 ;
}

D: 排成一行的骰子

题意
我们把 n个骰子从左到右排成一行
左侧第 i 个骰子显示 p i p_i pi , p i p_i pi表示这个骰子可以抛出的点数范围为[1, p i p_i pi],每个点抛出的概率相等
我们将选择k个相邻的骰子,分别投掷每个骰子,并计算所示数字的总和。
查找此总和的期望值的最大可能值。
思路
对每个骰子来说,每个点数抛出的概率相等为 1 / p i 1/p_i 1/pi
所以每个骰子的点数的期望为
1 / p i ∗ ( 1 + 2 + 3 + . . . . . + p i ) 1/p_i*(1+2+3+.....+p_i) 1/pi(1+2+3+.....+pi)
1 / p i ∗ ( p i ) ∗ ( p i + 1 ) / 2 1/p_i*(p_i) * (p_i+1)/2 1/pi(pi)(pi+1)/2
化简得
( 1 + p i ) / 2 (1 + p_i)/2 (1+pi)/2
题目求的是连续k个骰子的期望和的最大值
用前缀和优化一下即可

时间复杂度 O n On On

#include<bits/stdc++.h>
using namespace std ;
const int N = 1e6 + 10 ;
int n , k ;
double a[N] , s[N] ;

int main()
{
    cin >> n >> k ;
    for(int i = 1 ; i <= n ; i ++)
    {
        cin >> a[i] ;
        s[i] = s[i - 1] + (a[i] + 1.0) / 2.0 ;
    }
    
    double v = 0 ;
    for(int i = 1 ; i + k - 1 <= n ; i ++)
    {
        v = max(v , s[i + k - 1] - s[i - 1]) ;
    }
    
    printf("%.12lf",v) ;
    
    return 0 ;
}

E: 到处都是0

题意
求1到N(包括1和N)之间的整数的数目,
这些整数恰好包含K个非0数字
思路
在这里插入图片描述

首先我们把 n n n的每一位保存到一个数组里面

我们从这个数的最高位往最低位遍历

假设现在遍历到了最高位
0 0 0 a n − 1 a_{n}-1 an1的任意数字,记为x
x_ _ _ _ _ _ _ _ _ 后面有n-1个位置
通过动态规划可以计算出来

我们用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]来表示一共有 i i i位,最高位填 j j j,当前一共有 k k k个非 0 0 0数字的方案数
状态转移方程为
f [ i + 1 ] [ z ] [ k + ( z ! = 0 ) ] + = f [ i ] [ j ] [ k ] ; f[i + 1][z][k + (z != 0)] += f[i][j][k] ; f[i+1][z][k+(z!=0)]+=f[i][j][k];

x_ _ _ _ _ _ _ _ _ 后面有n-1个位置
所以该方案数为 f [ n ] [ x ] [ k ] f[n][x][k] f[n][x][k]
表示一共有 n n n位,最高位填的是 x x x,有 k k k个非 0 0 0数字的情况下的方案数

填完 a n a_n an之后,继续往这颗树上继续填,直到不能填为止

时间复杂度 O ( 100 n ) O(100n) O(100n)

#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;

const int N = 110;

int f[N][10][5] , m ;
char s[N] ;

void init()  // 求动态规划方程
{
    for (int i = 0; i <= 9; i ++ ) f[1][i][(i != 0)] = 1;

    for (int i = 1; i <= N - 10; i ++ )
        for (int j = 0; j <= 9; j ++ )
            for(int z = 0 ; z <= 9 ; z ++)
                for(int k = 0 ; k <= 3 ; k ++)
                    f[i + 1][z][k + (z != 0)] += f[i][j][k] ;
            
}

void dp()
{
    vector<int> nums;
    
    cin >> s + 1 >> m ;
    for(int i = strlen(s + 1) ; i >= 1 ; i --)
        nums.push_back(s[i] - '0') ;

    int res = 0;
    int last = 0;  // last表示之前高位填的数中有多少个非0数
    for (int i = nums.size() - 1; i >= 0; i -- )
    {
        int x = nums[i];     // 当前位的数字
        if(last > m) break ;  // 高位已经填了k+1个非0数 直接break即可
        
        //cout << i << " " << x << " " << last << '\n' ;
        
        for(int j = 0 ; j < x ; j ++)
            res += f[i + 1][j][m - last] ;
        
        //cout << res << '\n' ;
        
        if(x != 0) last ++ ;  // 统计高位的非0数

        if (!i && last == m) res ++ ;  // 如果这个数本身也符合题意  答案++
    }

    cout << res << '\n' ;
}

signed main()
{
    init();
    
    dp() ;
    
    return 0;
}

F: 路径数目

题意

小梁站在一个二维平面上。在一次操作中,他可以在正x方向移动1,或在正y方向移动1。
让我们定义一个函数f(r,c),如下所示:
f ( r , c ) f(r,c) f(rc)=从点(0,0)到点(r,c)的路径数
现在给你 r 1 , c 1 , r 2 , c 2 r1 , c1 , r2 , c2 r1,c1,r2,c2

∑ i = r 1 r 2 ∑ j = c 1 c 2 f ( i , j ) \sum_{i=r1}^{r2}{\sum_{j=c1}^{c2}f(i,j)} i=r1r2j=c1c2f(i,j)

思路

首先,从 ( 0 , 0 ) (0,0) (0,0)走到 ( i , j ) (i,j) (i,j)的方案数为 C ( i + j , i ) C(i+j,i) C(i+j,i)
表示必须走 i + j i+j i+j步,必须走 i i i步横着的或者是 j j j步竖着的

开始推式子

∑ i = r 1 r 2 ∑ j = c 1 c 2 f ( i , j ) \sum_{i=r1}^{r2}{\sum_{j=c1}^{c2}f(i,j)} i=r1r2j=c1c2f(i,j)

等价于
∑ i = r 1 r 2 ∑ j = c 1 c 2 C i + j i \sum_{i=r1}^{r2}{\sum_{j=c1}^{c2}C_{i+j}^{i}} i=r1r2j=c1c2Ci+ji

因为
C n + m + 1 m + 1 = ∑ i = 0 n C m + i m C_{n+m+1}^{m+1} = \sum_{i=0}^{n}{C_{m+i}^{m}} Cn+m+1m+1=i=0nCm+im

所以原式等于
∑ i = r 1 r 2 C i + c 2 + 1 i + 1 − C i + c 1 + 1 − 1 i + 1 \sum_{i=r1}^{r2}{C_{i+c2+1}^{i+1}-C_{i+c1+1-1}^{i+1}} i=r1r2Ci+c2+1i+1Ci+c1+11i+1

预处理阶乘和阶乘的逆元即可O n n n计算

时间复杂度 O n l o g n Onlogn Onlogn

#include<bits/stdc++.h>
#define int long long 
using namespace std ;
const int N = 3e6 + 10 , mod = 1e9 + 7 ;

int fact[N] , infact[N];
int qmi(int a , int b , int mod)
{
    int res = 1 ;
    while(b)
    {
        if(b & 1) res = (res * a) % mod ;
        b >>= 1 ;
        a = a * a  % mod ;
    }
    return res;
}
int c(int a , int b)
{
    if(b > a) return 0 ;
    return fact[a] % mod * infact[b] % mod * infact[a - b] % mod ;
}
void init(int n)
{
    fact[0] = infact[0] = 1 ;
    for(int i = 1 ; i <= n ; i ++)
    {
        fact[i] = fact[i-1] % mod  * i % mod ;
        infact[i] = infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
}

signed main()
{
    init(N - 10) ;
    int r1 , c1 , r2 , c2 ;
    cin >> r1 >> c1 >> r2 >> c2 ;
    
    int res = 0 ;
    for(int i = r1 ; i <= r2 ; i ++)
    {
        res = (res + c(i + c2 + 1 , i + 1) - c(i + c1 + 1 - 1 , i + 1)) % mod ;
        res = (res % mod + mod) % mod ;
    }
    
    cout << res << '\n' ;
    
    return 0 ;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值