AcWing Round #16

A. 统一大小写

题目链接


给定一个单词,单词中既可能包含大写字母也可能包含小写字母。

现在,要将单词进行统一大小写的操作,具体操作规则为:

  • 如果一个单词中包含的大写字母数量大于小写字母数量,则将所有字母统一为大写,例如 ViP 应改为 VIP
  • 如果一个单词中包含的大写字母数量小于小写字母数量,则将所有字母统一为小写,例如 HoUse 应改为 house
  • 如果一个单词中包含的大写字母数量等于小写字母数量,则将所有字母统一为小写,例如 maTRIx 应改为 matrix
输入格式

第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据占一行,包含一个由大小写字母构成的字符串 s s s。(注意,字符串可能只包含小写字母或只包含大写字母)。

输出格式

每组数据输出一行结果,一个字符串,表示统一大小写后的单词。

数据范围

本题共两个测试点。
小测试点,如样例所示。
大测试点满足: 1 ≤ T ≤ 30 1\leq T\leq 30 1T30,字符串长度取值范围 [ 1 , 100 ] [1,100] [1,100]

输入样例:
3
HoUse
ViP
maTRIx
输出样例:
house
VIP
matrix
题目分析:

字符串模拟。

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        string str;
        cin >> str;
        int a = 0, b = 0;
        for (auto c : str) if (c >= 'a') a ++ ;
        else b ++ ;
        
        if (b > a)
        {
            for (auto& c : str) c = toupper(c);
        }
        else
        {
            for (auto& c : str) c = tolower(c);
        }
        cout << str << endl;
    }
    return 0;
}

B. 截断数组

题目链接


给定一个长度为 n n n 的数组 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an

现在,要将该数组从中间截断,得到三个非空子数组。

要求,三个子数组内各元素之和都相等。

请问,共有多少种不同的截断方法?

输入格式

第一行包含整数 n n n

第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an

输出格式

输出一个整数,表示截断方法数量。

数据范围

前六个测试点满足 1 ≤ n ≤ 10 1\leq n\leq 10 1n10
所有测试点满足 1 ≤ n ≤ 1 0 5 , − 10000 ≤ a i ≤ 10000 1\leq n\leq 10^5,−10000\leq a_i\leq 10000 1n10510000ai10000

输入样例1:
4
1 2 3 3
输出样例1:
1
输入样例2:
5
1 2 3 4 5
输出样例2:
0
输入样例3:
2
0 0
输出样例3:
0

题目分析:

无标题.png
首先数组总和得是 3 3 3 的倍数,否则不可能分割成 3 3 3 分元素和相同的子数组。

从数组中选两个空隙进行分割,三段区间和都必须等于数组元素总和 s u m sum sum 的三分之一。

我们可以采用前缀和思想,先预处理前缀和数组 s,然后枚举第二刀的位置,在枚举第二刀的同时,计算出合法的第一刀的位置方案的数量,当第二刀位置合法时,就累加当前计算出的第一刀方案数,当第二刀枚举完后,总共的合法方案数也就计算出了。

这种方法不仅可以不重不漏的计算出合法方案数,并且时间复杂度是线性的。

注: 虽然前缀和大小不会爆 int,但总的方案数最坏为 ( 2 n − 1 ) \dbinom{2}{n-1} (n12),会爆 int,因此方案数需要用 long long 存。

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;

typedef long long LL;

int n;
int s[N];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &s[i]);
        s[i] += s[i - 1];
    }
    // 如果总和不是3的倍数,直接无解
    if (s[n] % 3) 
    {
        puts("0");
        return 0;
    }
    
    // 虽然前缀和不会爆int,但是方案数是在一个数组中
    // 任选两个位置切一刀,n个数,n-1个空隙,共(2, n-1)种方案,方案数会爆int
    LL res = 0;
    // 枚举第二刀的位置
    for (int i = 3, cnt = 0; i <= n; i ++ )
    {
        if (s[i - 2] == s[n] / 3) cnt ++ ;
        if (s[n] - s[i - 1] == s[n] / 3) res += cnt;
    }
    
    printf("%lld\n", res);
    return 0;
}

C. 子序列

题目链接


我们称一个序列是有序的,如果该序列是非严格单调递增序列或非严格单调递减序列。

现在,给定一个 n n n 个整数的序列 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an

请你找到该序列的最短非有序子序列。

注意,子序列不一定连续。

输入格式

第一行包含整数 n n n

第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an

输出格式

如果不存在非有序子序列,则输出一行 0 0 0

否则,首先在第一行输出一个整数,表示最短非有序子序列的长度,随后在第二行按顺序输出该子序列的各元素下标。

如果方案不唯一,输出任意合理方案均可。

数据范围

6 6 6 个测试点满足 1 ≤ n ≤ 10 1\leq n\leq 10 1n10,
所有测试点满足 1 ≤ n ≤ 1 0 5 , − 1 0 6 ≤ a i ≤ 1 0 6 1\leq n\leq 10^5,−10^6\leq a_i\leq 10^6 1n105106ai106

输入样例1:
5
67 499 600 42 23
输出样例1:
3
1 3 5
输入样例2:
3
1 2 3
输出样例2:
0
输入样例3:
3
2 3 1
输出样例3:
3
1 2 3
题目分析:

非有序子序列,最少由 3 3 3 个数构成,任何少于 3 3 3 个数构成的子序列都不可能是非有序的,故答案要求的最短非有序子序列,长度一定为 3 3 3

并且只有两种可能性,一种为 V 形,另一种为 形。

假设数组下标范围为 1 ∼ n 1\sim n 1n,我们可以枚举中间一个数的位置 2 ∼ n − 1 2\sim n-1 2n1,如果为 V 形,则说明在中间这个数的左边和右边,均存在一个比它大的数;如果是 形,则说明在中间这个数的左边和右边,均存在一个比它小的数,因为方案任意,我们可以处理出左右两边的最大数或最小数数组 lmax[], lmin[], rmax[], rmin 进行判断。

其中,lmax[i] 表示从 1 ∼ i 1\sim i 1i 中最大的那个数的下标,rmax[i] 表示从 i ∼ n i\sim n in 中最大的那个数的下标,其他同理。

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
int w[N];
int lmin[N], lmax[N], rmin[N], rmax[N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    
    // 预处理lmax, lmin, rmax, rmin
    lmin[1] = lmax[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        lmin[i] = lmax[i] = i;
        if (w[lmin[i - 1]] < w[i]) lmin[i] = lmin[i - 1];
        if (w[lmax[i - 1]] > w[i]) lmax[i] = lmax[i - 1];
    }
    
    rmin[n] = rmax[n] = n;
    for (int i = n - 1; i; i -- )
    {
        rmin[i] = rmax[i] = i;
        if (w[rmin[i + 1]] < w[i]) rmin[i] = rmin[i + 1];
        if (w[rmax[i + 1] > w[i]]) rmax[i] = rmax[i + 1];
    }
    
    // 枚举中间点
    for (int i = 2; i < n; i ++ )
    {
        if (w[lmax[i - 1]] > w[i] && w[rmax[i + 1]] > w[i])
        {
            printf("3\n%d %d %d\n", lmax[i - 1], i, rmax[i + 1]);
            return 0;
        }
        if (w[lmin[i - 1]] < w[i] && w[rmin[i + 1]] < w[i])
        {
            printf("3\n%d %d %d\n", lmin[i - 1], i, rmin[i + 1]);
            return 0;
        }
    }
    puts("0");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值