「模拟赛」暑期集训CSP提高模拟5(7.22)
\(140pts, Rank24\)
题目列表:
简单的序列
\(100pts\)
题意:
gtm1514 不喜欢序列。但是一天他拿到了一个序列。
这个序列非常杂乱,于是 gtm1514 想要整理一下这个序列。但是由于他非常的菜,他只会进行一个操作:选择一个 \(ai\),把它变成 \(⌊\frac{a_i}2⌋\)。
gtm1514 又菜又爱摆,他想让你找到使得数列严格递增的最小操作次数。如果无解,输出 −1。
对于 100% 的数据,\(t≤10^4,n≤30,a_i≤2×10^9\)。
正解:
直接暴力即可,实际复杂度 \(O(t\ n \log_{2}{a_i})\)
将 \(i\) 从 \(n\) 到 1 遍历,每当遇到一个 \(i\) 使得 $a_i>=a_{i+1} $, 不断计算 $a_i= \lfloor {\frac{a_i}2} \rfloor $,直到 \(a_i<a_{i+1}\)。
code:
#include<bits/stdc++.h>
using namespace std;
const int N = 50;
int T, n, a[N];
int work(int &now, int ne){
int cnt = 0;
while(now >= ne){now /= 2, cnt++;}
return cnt;
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
}
int ans = 0;
for(int i=n-1; i>=1; i--){
ans += work(a[i], a[i+1]);
if(i != 1 and a[i] == 0){
ans = -1;
break;
}
}
printf("%d\n", ans);
}
return 0;
}
简单的字符串
\(30 pts\),最唐的一次挂分
题意:
现在有一个字符串,但是它长得还是十分甚至九分混乱邪恶。gtm1514 想要整理一下这个字符串。
他发明了一个神奇的操作:一次操作使得字符串缩短一位,新字符串的第 i 位可以与原串的第 i 或 i+1 位相同。
他想知道最少经过多少次操作可以使整个字符串全相同,即只有一种字母。
赛时分析:
一眼有点感觉,可做。于是想想想,假假假。手模了半个小时之后不知道怎么着就想到正解了,突然就跳出来了。结果打完很开心地以为能 \(A\) 两个题了,结果 map 数组范围开错了......
正解:
每个英文字母的扩展的次数其实是它到与自己相同的字母的上一个位置的距离。
求出原每个位置上的前驱,然后枚举 26 个英文字母的所有位置,对于每一种英文字母,满足条件所需即为所有位置到前驱距离以及最后一个位置到原序列最后一个字母的距离的最大值,答案就是 26 个字母的所需的最小值。
code:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n;
char s[N];
// map<char, int>la;
int cnt[130][N], la[200];
int work(char c){
int num = n - la[c];
for(int i=1; i<=cnt[c][0]; i++){
num = max(num, cnt[c][i] - cnt[c][i-1] - 1);
}
return num;
}
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
cin>>(s+1);
n = strlen(s+1);
for(int i=1; i<=n; i++){
// pre[i] = la[s[i]];
la[s[i]] = i;
cnt[s[i]][++cnt[s[i]][0]] = i;
}
int ans = N;
for(char c='a'; c<='z'; c++){
if(!cnt[c][0]) continue;
ans = min(ans, work(c));
}
printf("%d", ans);
return 0;
}