A. 统一大小写
给定一个单词,单词中既可能包含大写字母也可能包含小写字母。
现在,要将单词进行统一大小写的操作,具体操作规则为:
- 如果一个单词中包含的大写字母数量大于小写字母数量,则将所有字母统一为大写,例如
ViP
应改为VIP
。 - 如果一个单词中包含的大写字母数量小于小写字母数量,则将所有字母统一为小写,例如
HoUse
应改为house
。 - 如果一个单词中包含的大写字母数量等于小写字母数量,则将所有字母统一为小写,例如
maTRIx
应改为matrix
。
输入格式
第一行包含整数 T T T,表示共有 T T T 组测试数据。
每组数据占一行,包含一个由大小写字母构成的字符串 s s s。(注意,字符串可能只包含小写字母或只包含大写字母)。
输出格式
每组数据输出一行结果,一个字符串,表示统一大小写后的单词。
数据范围
本题共两个测试点。
小测试点,如样例所示。
大测试点满足:
1
≤
T
≤
30
1\leq T\leq 30
1≤T≤30,字符串长度取值范围
[
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
1≤n≤10。
所有测试点满足
1
≤
n
≤
1
0
5
,
−
10000
≤
a
i
≤
10000
1\leq n\leq 10^5,−10000\leq a_i\leq 10000
1≤n≤105,−10000≤ai≤10000。
输入样例1:
4
1 2 3 3
输出样例1:
1
输入样例2:
5
1 2 3 4 5
输出样例2:
0
输入样例3:
2
0 0
输出样例3:
0
题目分析:
首先数组总和得是
3
3
3 的倍数,否则不可能分割成
3
3
3 分元素和相同的子数组。
从数组中选两个空隙进行分割,三段区间和都必须等于数组元素总和 s u m sum sum 的三分之一。
我们可以采用前缀和思想,先预处理前缀和数组 s
,然后枚举第二刀的位置,在枚举第二刀的同时,计算出合法的第一刀的位置方案的数量,当第二刀位置合法时,就累加当前计算出的第一刀方案数,当第二刀枚举完后,总共的合法方案数也就计算出了。
这种方法不仅可以不重不漏的计算出合法方案数,并且时间复杂度是线性的。
注: 虽然前缀和大小不会爆 int
,但总的方案数最坏为
(
2
n
−
1
)
\dbinom{2}{n-1}
(n−12),会爆 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
1≤n≤10,
所有测试点满足
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
1≤n≤105,−106≤ai≤106。
输入样例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
1∼n,我们可以枚举中间一个数的位置
2
∼
n
−
1
2\sim n-1
2∼n−1,如果为 V
形,则说明在中间这个数的左边和右边,均存在一个比它大的数;如果是 ∧
形,则说明在中间这个数的左边和右边,均存在一个比它小的数,因为方案任意,我们可以处理出左右两边的最大数或最小数数组 lmax[], lmin[], rmax[], rmin
进行判断。
其中,lmax[i]
表示从
1
∼
i
1\sim i
1∼i 中最大的那个数的下标,rmax[i]
表示从
i
∼
n
i\sim n
i∼n 中最大的那个数的下标,其他同理。
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;
}