2023牛客暑假多校4 题解AFJL | JorbanS

A-Bobo String Construction

题意 给定 01t,输出一个长度为 n01s 使得 t + s + t 中间只能找到 2 个 t 字符串

题解

  • 如果 s 全为 0,只有以下情况使得条件不成立,此时若 s 全为 1 则满足条件
  • 如果 t 全为 1,同上理,s 全为 0 即可
00000|00000|00000
  t     s     t
100001|0000|100001
   t    s     t
100010|00|100010
   t    s    t
010001|00|010001
   t    s    t
1000100|0|1000100
   t    s    t

以下是官方题解的证明:
在这里插入图片描述

暴力匹配

#include <iostream>
#define endl '\n'

using namespace std;
int n;

void solve() {
    string t; cin >> n >> t;
    string s(n, '0');
    bool flag = 0;
    string tst = t + s + t;
    if (tst.find(t, 1) != t.size() + s.size()) flag = 1;
    while (n --) cout << flag;
    cout << endl;
}

int main() {
    int T; cin >> T;
    while (T --) solve();
    return 0;
}

KMP 匹配

#include <iostream>

using namespace std;
const int N = 1e3 + 2;
int n, m, ne[N];

void solve() {
    string t;
    cin >> n >> t;
    m = t.size();
    string s(n, '0');
    string tst = '0' + t + s + t;
    t = '0' + t;
    // 求 pmt
    for (int i = 2, j = 0; i <= m; i ++) {
        while (j && t[i] != t[j + 1]) j = ne[j];
        if (t[i] == t[j + 1]) j ++;
        ne[i] = j;
    }
    bool flag = false;
    // 文本串匹配模式串
    for (int i = 1, j = 0; i <= n + m * 2; i ++) {
        while (j && tst[i] != t[j + 1]) j = ne[j];
        if (tst[i] == t[j + 1]) j ++;
        if (j == m && i != m && i != n + m * 2) {
            flag = true;
            break;
        }
    }
    while (n --) cout << flag;
    cout << endl;
}

int main() {
    int T; cin >> T;
    while (T --) solve();
    return 0;
}

F-Election of the King

题意 n 个人,n - 1 轮投票,每次淘汰一个人,输出最后留下的人的下标。每次投票每个人选择和自己权值相差最大的,如果相同取全局最大的,最后如果出现平局,淘汰权值最大的人

题解 先排序,每次只能淘汰两端的,取首位的平均数,平均数之下的会投最右端的,平均数及其之上的会投最左端的,故比较平均数左右的人数,淘汰对应的人

#include <iostream>
#include <vector>
#include <algorithm>
#define endl '\n'
#define aa first
#define bb second

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 2;
int n;
vector<pii> v;

int solve() {
    cin >> n;
    for (int i = 0; i < n; i ++) {
        int x; cin >> x;
        v.push_back({x, i + 1});
    }
    sort(v.begin(), v.end());
    int l = 0, r = n - 1;
    while (l < r) {
        int ll = l, rr = r;
        int x = v[ll].aa + v[rr].aa;
        int y;
        while (ll < rr) {
            int mid = ll + rr >> 1;
            y = v[mid].aa * 2;
            if (y > x) rr = mid;
            else ll = mid + 1;
        }
        ll --;
        if (ll - l + 1 >= r - ll) r --;
        else l ++;
    }
    return v[l].bb;
}

int main() {
    cout << solve() << endl;
    return 0;
}

J-Qu’est-ce Que C’est?

题意 求一个长度为 n 数序列,使得每个数的绝对值小于 m,且任意长度不为 1 的区间的区间和非负,求有多少种满足的序列

Tag dp

状态表示
f [ i ] [ j ] 表示填了前 i 个数、后缀最小值为 j 的合法数量, j < 0 是可以的即表示最后一个数为负,且之前没有包括这个数负和区间 f[i][j]表示填了前i个数、后缀最小值为j的合法数量,j<0是可以的即表示最后一个数为负,且之前没有包括这个数负和区间 f[i][j]表示填了前i个数、后缀最小值为j的合法数量,j<0是可以的即表示最后一个数为负,且之前没有包括这个数负和区间

从 i − 1 个数转移,第 i 个数填 k ,从 f [ i − 1 ] [ j ] 转移到 f [ i ] [ m a x ( j , 0 ) + k ] 从i-1个数转移,第i个数填k,从f[i-1][j]转移到f[i][max(j,0)+k] i1个数转移,第i个数填k,从f[i1][j]转移到f[i][max(j,0)+k]

暂时无 code

L-We are the Lights

题意 每次选择一行或一列开或关灯,输出最后有多少灯开着

题解 结构体记录操作,只保留每行每列的最后一次操作,故每个点只能被改变两次

cnt 记录有多少行/列开灯,last 记录第几行/列的最后一次操作序号,st 记录每行/列最后的状态

操作res 变化
行开+ 关着的列
行关- 开着的列
列开+ 关着的行
列关- 开着的行
#include <iostream>
#define endl '\n'

using namespace std;
typedef long long ll;
const int N = 1e6 + 2;
ll l[2], cnt[2], last[2][N]; // 0row 1col
bool st[2][N]; // 0row 1col

struct Op {
    bool type; // 0row 1col
    int pos;
    bool status; // 0off 1on
} ops[N];

ll solve() {
    int q; cin >> l[0] >> l[1] >> q;
    for (int i = 0; i < q; i ++) {
        string s1, s2; int x; cin >> s1 >> x >> s2;
        ops[i] = {s1 == "column", x, s2 == "on"};
        st[s1 == "column"][x] = s2 == "on";
        last[s1 == "column"][x] = i;
    }
    ll res = 0;
    for (int i = 0; i < q; i ++) {
        if (st[ops[i].type][ops[i].pos] ^ ops[i].status || i != last[ops[i].type][ops[i].pos]) continue;
        if (ops[i].status) {
            cnt[ops[i].type] ++;
            res += l[1 - ops[i].type] - cnt[1 - ops[i].type];
        }
        else {
            res -= cnt[1 - ops[i].type];
        }
    }
    return res;
}

int main() {
    cout << solve() << endl;
    return 0;
}

法二:逆序操作cnt 表示还剩多少行/列未被操作过,只要用 st 记录是否被操作过即可,操作过的 cnt --

#include <iostream>
#define endl '\n'

using namespace std;
typedef long long ll;
const int N = 1e6 + 2;
ll n, m, cnt[2]; // 0row 1col
bool st[2][N]; // 0row 1col

struct Op {
    bool type; // 0row 1col
    int pos;
    bool status; // 0off 1on
} ops[N];

ll solve() {
    int q; cin >> n >> m >> q;
    cnt[0] = n, cnt[1] = m;
    for (int i = 0; i < q; i ++) {
        string s1, s2; int x; cin >> s1 >> x >> s2;
        ops[i] = {s1[0] == 'c', x, s2[1] == 'n'};
    }
    ll res = 0;
    for (int i = q - 1; i >= 0; i --) {
        if (st[ops[i].type][ops[i].pos]) continue;
        st[ops[i].type][ops[i].pos] = true;
        if (ops[i].status) res += cnt[1 - ops[i].type];
        cnt[ops[i].type] --;
    }
    return res;
}

int main() {
    cout << solve() << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JorbanS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值