A. Three Piles of Candies
两个小朋友AB分三堆糖果,AB各自先取一堆,剩下一堆两人可以任意分配。取完后,糖果多的那个人必须扔掉多余的糖果,保持和糖果少的人数量相同。求两人获得的最大糖果数量。
解:
要使他们糖果数最多,则让他们获得的糖果数相近,于是两人先取较小的两堆,最大的一堆用来分配,便可以保证两人获得糖果数相等或之差为1,则答案便为糖果总数sum/2.
B. Odd Sum Segments
给出一个n个数的序列,求让你把他们分为k个连续的不相交的子序列, 使得每个子序列中数字的总和都为奇数, 输出每个子序列的最后一个数的位置。
解:
令sum为n个数之和,首先讨论sum与k的关系, 若sum为奇数而k为偶数,或sum为偶数k为奇数,则必定失败(由奇偶相乘性质);当sum与k同奇偶时,答案必为序列的前k处可以划分的位置,具体证明如下:
假设答案不是前k处可以划分的位置,必存在某一处划分i,为原前i-1处划分加上原第i处个划分的一个偶数部分,而原第i处划分减去一个偶数部分,仍为一处和为奇数的划分,且该划分为当前最优的下一个划分,不影响其后的原划分方法,不可能有更优解,故最优解必为前k处划分。
代码如下:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int q, n, k;
LL a[200005], sum, ans[200005];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> q;
while(q--) {
sum = 0;
cin >> n >> k;
int kk = k;
int cnt = 0;
for(int i = 0; i < n; i++) cin >> a[i], sum += a[i];
if((sum % 2) && (k % 2 == 0)) //不符合题意
cout << "NO\n";
else if((sum % 2 == 0) && (k % 2))
cout << "NO\n";
else if(sum % 2) { //sum与k同奇
if(k == 1) { //此处需要特判
cout << "YES\n";
cout << n << "\n";
continue;
}
LL he = 0; //求出前k-1处和
for(int i = 0; i < n; i++) {
he += a[i];
if(he % 2) {
k--; he = 0;
ans[cnt++] = i+1; //存在答案数组中
}
if(k == 1) break;
}
if(k == 1) {
cout << "YES\n";
for(int i = 0; i < kk - 1; i++)
cout << ans[i] << ' ';
cout << n << "\n";
}
else cout << "NO\n";
}
else if(sum % 2 == 0){ //sum与k同偶, 同理
LL he = 0;
for(int i = 0; i < n; i++) {
he += a[i];
if(he % 2) {
k--; he = 0;
ans[cnt++] = i+1;
}
if(k == 1) break;
}
if(k == 1) {
cout << "YES\n";
for(int i = 0; i < kk-1; i++)
cout << ans[i] <<' ';
cout << n << "\n";
}
else cout << "NO\n";
}
}
return 0;
}
C. Robot Breakout
有n个机器人想要逃脱,给出他们的位置坐标x, y和四个运动参数,分别代表他们能够行动的方向,参数为1即可以向该方向移动,为0则不能移动。求一个所有的机器人都能移动到的点。
解:
我们可以设定四个参数来控制最后的答案范围,即最小的x坐标,最大的x 坐标, 最小的y坐标, 最大 的y坐标,分别记作xl, xr, yd, yu。当读入一个点后,更新这四个坐标,若机器人不能向左走,则当前机器人的x坐标左边的区域都不能走,同时若xl小于x, 则更新它为x;同理,若机器人不能向右走,则当前机器人的x坐标有边的区域都不能走,同时若xr大于x, 则更新它为x…最后判断xl,xr,yu,yd是否冲突, 不冲突就输出该范围内的任意答案。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define inf 1e5
int q, n, xl = -inf, xr = inf, yd = -inf, yu = inf;
struct rbt{
int x, y;
int f[4];
}a[100005];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> q;
while(q--) {
xl = -inf, xr = inf, yd = -inf, yu = inf;
cin >> n;
for(int i = 0; i < n; i++) {
cin >> a[i].x >> a[i].y >> a[i].f[0] >> a[i].f[1] >> a[i].f[2] >> a[i].f[3];
if(!a[i].f[0])
xl = xl > a[i].x ? xl : a[i].x;
if(!a[i].f[1])
yu = yu > a[i].y ? a[i].y : yu;
if(!a[i].f[2])
xr = xr > a[i].x ? a[i].x : xr;
if(!a[i].f[3])
yd = yd > a[i].y ? yd : a[i].y;
//cout << xl << ' '<< xr << ' '<< yu << ' '<< yd << endl;
}
if(xl > xr || yu < yd)
cout << 0 << "\n";
else {
cout << 1 << ' ' << xl << ' ' << yd << "\n";
}
}
return 0;
}
D1. RGB Substring (easy version)
D2. RGB Substring (hard version)
题目相同,数据范围不同。
给定一个长度为n的字符串s,只含有R、G、B三种字母,求一个长度为k的连续子串p, 使得p能够更改最少的字母而成为模板串”RGBRGBRGB…“的一个子串。
D1:n<=2000 D2:n<=2×105
解:如果想更改最少的字母,由题可知,模板串只有三种模式,以R开头循环,以G开头循环和以B开头循环,则我们可以构造三个模式的模板rgb、gbr、brg,然后分别将它与s比较, 若某位置相同,则设立对应的数组r,g,b,将相应位置置为1, 若不相同则置为0。再对三个数组分别求一次前缀和。然后间隔k个位置作差一次,差值即为该连续k个位置能够匹配的字符,再以k减去,即为需要改变的字符。途中不断更新最小值,即为答案。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int q, n, k;
string s = "";
string rgb = "RGB";
string gbr = "GBR";
string brg = "BRG";
int r[N], g[N], b[N], Min = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> q;
while(rgb.size() <= N) rgb += "RGB"; //建立三种模式串
while(gbr.size() <= N) gbr += "GBR";
while(brg.size() <= N) brg += "BRG";
while(q--) {
Min = 0x3f3f3f3f;
cin >> n >> k;
memset(r, 0, (n+2)*sizeof(int)); //清零
memset(g, 0, (n+2)*sizeof(int));
memset(b, 0, (n+2)*sizeof(int));
cin >> s;
s = " " + s;
//cout << s << endl;
for(int i = 1; i <= s.size(); i++) { //求匹配数量
if(s[i] == rgb[i]) r[i] = 1;
if(s[i] == gbr[i]) g[i] = 1;
if(s[i] == brg[i]) b[i] = 1;
}
for(int i = 2; i <= s.size(); i++) {
r[i] += r[i-1];
g[i] += g[i-1];
b[i] += b[i-1];
}
for(int i = 0, j = k; i+k <= s.size(); i++, j++){ //更新答案
Min = min(k-(r[j]-r[i]), Min);
Min = min(k-(g[j]-g[i]), Min);
Min = min(k-(b[j]-b[i]), Min);
}
cout << Min << "\n";
}
return 0;
}
E. Connected Component on a Chessboard
在一个109 109的国际象棋棋盘中(左上角为白色),给定两个数b, w,求一个联通块,使其中含有b个黑色块和w个白色块。(b+w<2×105 )
解:
观察国际象棋棋盘可以得出,想要选最多的黑色的话,第一个块选黑色,周围都是白色,任选其一,白色周围除了之前选过的黑色之外还有3块黑色,全选上,然后再选1白,再选3黑…则黑色最多达到白色的3n + 1块,白色同理。
则若输入不符合该限制,输出NO。若符合限制,由于b+w较小,棋盘较大,为了输出方便,我们将所有的选择限制在3行之内。
1,若b==w,则输出任意一行连续b+w个格子位置。
2,若b>w,则先输出以黑方格开头的某行(这里选第2行)的前2w + 1个格子位置,此时w已满足,b已经有w+1个,则再输出该行上下两行中相邻的黑格子位置,输出够b个为止。
3,若w>b,同理,输出以白方格开头的某行(这里选第3行)的前2*b + 1个格子位置,此时b已满足,w已经有b+1个,则再输出该行上下两行中相邻的白格子位置,输出够w个为止。
代码:
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int q, b, w;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> q;
while(q--) {
cin >> b >> w;
if(b > (3*w + 1) || w > (3*b + 1)) {
cout << "NO\n";
continue;
}
cout << "YES\n";
if(b==w){
for(int i = 1; i <= w<<1 ;i++) {
cout << i << ' ' << 2 <<"\n";
}
}else if(b > w) {
for(int i = 1; i <= (w<<1|1); i++) //输出第3行黑白格子
cout << i << ' ' << 2 <<"\n";
b -= w+1;
if(!b) continue;
//cout<<b<<'-'<<endl;
for(int i = 2; i <= w<<1; i+=2) { //输出1、3行黑格子
cout << i << ' ' << 1 <<"\n";
b--; if(!b) break;
cout << i << ' ' << 3 <<"\n";
b--; if(!b) break;
}
}else {
for(int i = 1; i <= (b<<1|1) ;i++) //输出第3行黑白格子
cout << i << ' ' << 3 <<"\n";
w -= b+1;
if(!w) continue;
for(int i = 2; i <= b<<1; i+=2) { //输出2、4行白格子
cout << i << ' ' << 2 <<"\n";
w--; if(!w) break;
cout << i << ' ' << 4 <<"\n";
w--; if(!w) break;
}
}
}
return 0;
}
F. K-th Path
听说是个第k短路模板题, 咱还没学(汗)
有空补上咕咕咕
小白第一次写题解,代码也不够精简,如有错误欢迎大佬指出qwq~