Codeforces Round #616 (Div. 2)
A
最开始写挂了,打算从后往前截断到第一个奇数,再从前往后截断到数码和为偶数,不过并通过不了test2。
后来发现如果末尾一定要是奇数,那么前面一定还得有一个奇数才能使得数码和为偶数。所以只需要对串搜索找两个奇数就好了。
还是写挂了一发,原因有可能是因为在main函数中开了char ans[2]
但是输出的时候用了%s
,而局部变量的初始化并不一定满足能够在ans + 2
的位置成功截断。
B
对增区间而言,其中每一个元素都要满足 a [ i ] > = i a[i] >= i a[i]>=i (从0开始计数)。一旦某个元素不满足,那么这段区间无论如何都不可能都不能成为一个严格的增区间,进而比这个在元素之后的所有位置也都不可能成为峰点;类似地可以推出减区间的情况。用两个flag数组记录前缀后缀的情况,最后枚举所有可能的峰点位置,用flag1 & flag2
的值判断是否可行即可。
C
大体地考虑一下所有情况之间的共同点可以发现,那些可行区间的长度并没有发生变化,只是在大区间[1,n]
上左右平移,所以我们只需要具体分析我们能取到的元素到底在什么位置。
令 k = m i n ( m − 1 , k ) , u = m − 1 − k k= min(m - 1, k), u = m - 1 -k k=min(m−1,k),u=m−1−k,我们可以精控而获得的起始序列(从1开始计数)为[1 + i, n - k + i], i = 0, 1, ..k
,之后因为剩下u个人可以不受控制地随意选择,那么我们可能获得的最终序列又为[1 + i + j, n - k - u + i + j], j = 0, 1, ..., u
,而面对这个最终序列的"我"是有智力的,所以会选择两端中较大的一个。注意到题目给定的范围暗示着二重循环,所以可得最终计算式:
k = min(k, m - 1); int u = m - 1 - k, d = n - k - u - 1;
int ans = 0;
for(int i = 0; i <= k; i++){
int res = 0x3f3f3f3f;
//在大的里面找最坏的情况
for(int j = 0; j <= u; j++) res = min(res, max(a[i + j], a[i + j + d]));
//最大化最小值
ans = max(ans, res);
}
补题
D
昨晚想了一阵子但是并没有什么想法,加上有点困和这道题就过了二十来个人…所以我打完三题就溜了…
正确的做法是从元素种数入手,寻找母串一定拥有不可划分的相似串的充要条件。
s: a-b
,那么就可以构造不可划分串t: b-a
,其中-
是相同的子串,那么无论如何对s、t进行划分都不可能使得每一部分都互相相似;而如果两端元素相同,那么怎样构造都不能构造出一个不可划分串。-
表示s: a-b-c-a
,那么构造t: c-a-a-b
就获得了一个不可划分串。知道了这些充要条件后判断就变的很容易了,当查询区间内元素种数的时候直接用26个字母的前缀和进行O(26)的查询即可。
总体来说这道题不算特别难的算法题,但是比较考验分析、构造
的思维能力…
以及一定的场上打题策略
代码
const int maxn = 2e5 + 10;
char s[maxn];
//cnt[i][j]表示位置i及其之前的出现字符 j+'a' 的个数
int cnt[maxn][26];
int main(){
// Fast;
scanf("%s", s + 1); int n = (itn)strlen(s + 1);
for(int i = 1; i <= n; i++){
char c = s[i];
for(int j = 0 ; j < 26; j++){
if(j == c - 'a') cnt[i][j] = cnt[i - 1][j] + 1;
else cnt[i][j] = cnt[i - 1][j];
}
}
int q; scanf("%d", &q);
for(int i = 0; i < q; i++){
int l, r, flag = 0; scanf("%d%d", &l, &r);
if(l == r) flag = 1;
else if(s[l] != s[r]) flag = 1;
else{
int temp = 0;
for(int i = 0; i < 26; i++) if(cnt[r][i] - cnt[l - 1][i] > 0) temp++;
if(temp >= 3) flag = 1;
}
if(flag) puts("Yes");
else puts("No");
}
return 0;
}