Codeforces Round #535 Div.3
Codeforces Round #535 Div.3 题解
题目编号 | A | B | C | D1 | D2 | E | F |
---|---|---|---|---|---|---|---|
完成情况 | √ | √ | ★ | √ | √ | √ | - |
(更新ing)
C. Robot Breakout
题目大意:一个边界xy坐标为-1e51e5的地图上有n个已知坐标的机器人,每个机器人可以无限次执行向上下左右走一格的某几个(04个)操作。问是否存在一个点,所有机器人都可以移动到那里。存在输出1和这个点的坐标,不存在输出0
题解:2000人A的题居然不会,好菜啊(还好没打这一场
比较显然的一步是通过每个机器人的操作限制和当前坐标可以得到他能够走到的范围
于是陷入二维修改查询无法自拔。。。
实际上只需要记录一下最小最大xy就可以了……
xy分开考虑是这类题的常见思想
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
int max(int a, int b){return a > b ? a : b;}
int min(int a, int b){return a < b ? a : b;}
void swap(int &a, int &b){int tmp = a;a = b;b = tmp;}
int lowbit(int &x){return x & (-x);}
void read(int &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
}
const int INF = 100000;
int q, n, mi_x, ma_x, mi_y, ma_y;
int main()
{
read(q);
for(;q;--q)
{
mi_x = mi_y = -INF, ma_x = ma_y = INF;
read(n);
for(int i = 1;i <= n;++ i)
{
int tmpx, tmpy;
read(tmpx), read(tmpy);
int tmp1, tmp2, tmp3, tmp4;
read(tmp1), read(tmp2), read(tmp3), read(tmp4);
if(tmp1 == 0) mi_x = max(mi_x, tmpx);
if(tmp2 == 0) ma_y = min(ma_y, tmpy);
if(tmp3 == 0) ma_x = min(ma_x, tmpx);
if(tmp4 == 0) mi_y = max(mi_y, tmpy);
}
if(mi_x > ma_x || mi_y > ma_y) printf("0\n");
else printf("1 %d %d\n", mi_x, mi_y);
}
return 0;
}
D2. RGB Substring (hard version)
题目大意:给定一个只含'R''G''B'三个字符的长度为n字符串,给定一个整数k,可以修改字符串中的字符为'R''G''B'中的任意一个,问最少需要修改多少次能使字符串中的某个长度为k的子串成为标准串”RGBRGBRGBRGBRGBRGBRGB……“的子串
题解:虽然没看题解A了,但是觉得这题蛮有意思的,如果没有D1(本题easy版本,\(O(nk)\)做法)的提示,下一次碰见D2这道题不一定能想到,所以写一写
考虑\(O(nk)\)做法:对于原串的某个后缀,如果其首字符确定,则后缀为标准船的修改次数是确定的。于是我们可以枚举首字符和他的位置,向后遍历k个,取所有修改次数的最小值。
对于1e5的数据范围,感觉上应该是dp,发现如果包含k这个限制,复杂度会爆炸,如果不给k开一维而是强制dp状态满足长度k,又无法实现转移。直接考虑线长度k进行dp是有相当难度的。
这时候我去做了D1,想出了nk的做法,那么这个题的正解一定与某个字符确定,他的前缀/后缀修改次数都是确定的这个结论有关。既然是前缀后缀,我们能不能通过前缀差/后缀差求得长度为k的区间修改次数?可以!以后缀为例,假设位置i确定为R时,位置j > i确定为G,那么i确定为R时j以后的修改次数与j为G时j以后的修改次数是相等的。
也就是说,我们只要得知某个位置字符修改为R/G/B后后缀所需修改次数,就可以O(1)得到以这个字符开始k长子串的修改次数!而后缀所需修改次数显然可以\(O(n)\)dp求得。
从后往前dp不是很习惯,所以我把后缀改成了前缀。这样就可以从前往后dp了。
想的时候还想出另一个idea,双指针。但那个时候还没有意识到一个字符确定,他的前缀/后缀确定,修改次数确定这个结论,没有继续深入。现在想想,双指针也是可行的。开始表演口技(不是。
其实整个串只有三种改法:第一个字符为R,第一个字符为G,第一个字符为B。对于每一种,我们拿两个指针移动,保持间距为k,每次l + 1,r + 1,删除l的影响并加入r + 1的影响就可以了(然而并没有写)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
int max(int a, int b){return a > b ? a : b;}
int min(int a, int b){return a < b ? a : b;}
void swap(int &a, int &b){int tmp = a;a = b;b = tmp;}
int lowbit(int &x){return x & (-x);}
void read(int &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
}
const int INF = 0x3f3f3f3f;
const int MAXN = 200000 + 10;
//dp[i][0/1/2]表示第i个是 0/1/2时1~i需要变多少个
//转移:dp[i][j] = dp[i-1][j-1]
int q, n, k, num[MAXN], dp[MAXN][3], ans;
char s[MAXN];
int main()
{
read(q);
for(;q;-- q)
{
read(n), read(k), ans = INF;
scanf("%s", s + 1);
for(int i = 1;i <= n;++ i)
if(s[i] == 'R') num[i] = 0;
else if(s[i] == 'G') num[i] = 1;
else num[i] = 2;
for(int i = 1;i <= n;++ i)
for(int j = 0;j <= 2;++ j)
if(num[i] == j) dp[i][j] = dp[i - 1][(j + 2) % 3];
else dp[i][j] = dp[i - 1][(j + 2) % 3] + 1;
for(int i = 1;i <= n - k + 1;++ i)
for(int j = 0;j <= 2;++ j)
ans = min(ans, dp[i + k - 1][(j + k - 1) % 3] - dp[i - 1][(j + 2) % 3]);
printf("%d\n", ans);
}
return 0;
}
//E题挺简单的,不知道为什么只有几百人过,鸽了鸽了