CodeForces1196(div 3ABCDE)记录

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行)的前2
w + 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~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值