LOADING

加载过慢请开启缓存 浏览器默认开启

牛客暑期多校训练营4

呃呃这场签到一时半会没写出来就去睡觉了,睡醒已经结束,然后一题没开
更新至L,F,A,J…
(好无聊好无聊好无聊呜呜呜7.30)

题L

题意:给定一个 $n * m$ 排布的灯,有 $q$ 次操作,每次操作可以 $打开/关闭$ 某具体 $行/列$ , 问最后有多少灯是打开的($1<=n,m,q<=1e6$)。

解:由于后面的操作会对当前操作有影响,所以不妨我们对操作倒着看,这时候第一次打开某一行或列必然是增加答案的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
int row[N], col[N];
struct S{
    string x1, x2; int x;
}s[N];
int main(){
    int n, m, k; cin >> n >> m >> k;
    ll ans = 0;
    for(int i = 1; i <= k; ++ i){
        cin >> s[i].x1 >> s[i].x >> s[i].x2;
    }
    for(int i = k; i >= 1; -- i){
        auto t = s[i];
        if(t.x1 == "row"){
            if(row[t.x] == 0){
                row[t.x] = 1;
                if(t.x2 == "on") ans += m;
                n --;
            }
        }else {
            if(col[t.x] == 0){
                col[t.x] = 1;
                if(t.x2 == "on") ans += n;;
                m --;
            }
        }
    }
    cout << ans;
}

题F

题意:$n$ 个候选人每人都有一个 $a_i (political$ $tendencies)$,有 $n - 1$ 轮投票,每轮投票中未出局的候选人可以给一个人投票,票数最多者出局,其中对于 $i$ 这个候选人而言,他会投给 $j$ 候选人,当且仅当存在最大的 $|a_i - a_j|$, 如果同时存在了两个相同最大值的$|a_i - a_j|$ , 那么优先投给$a_j$ 较大的人。问最后剩下来的是几号候选人。

解:由题意可得,每轮出局的必然是剩余候选人中 $a_i$ 值是 $最大/最小$, 那么我们可以对 $a_i$ 排完序后对两端用双指针,将两个指针与中间候选人的 $a_{mid}$ 进行比较。 最终复杂度为 $O(n)$

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
pair<int, int> p[N];
int main(){
    int n; cin >> n;
    for(int i = 1; i <= n; ++ i){
        int x; cin >> x;
        p[i] = {x, i};
    }
    sort(p + 1, p + 1 + n);
    int l = 1, r = n;
    while(l < r){
        int mid = l + r >> 1;
        if(2 * p[mid].first > p[l].first + p[r].first)
            l ++;
        else 
            r --;
    }
    cout << p[l].second;
}

题A

题意:已知一个消息字符串 $s$, 其加密字符串为 $t+s+t$,其中字符串 $t$ 为识别字符串,用于解码。但对于本题中读取加密字符串是一个个字符读取,当加密字符串 $0101101010$, 已知$t=010$, 其标准的消息字符串 $s=1101$, 但由于读取过程的问题,在计算机读取了加密字符到 $01011010$ 时就会停止并且获得消息字符串 $s=11$ 与需求不符,存在歧义。 题目给定消息字符串 $s$ 的长度和识别字符串 $t$,要求任意构造一个不存在歧义的消息字符串 $s$($1<=|t|, n<=1000$, 本题中字符串由 $01$ 构成)。

解:由于可以任意构造且字符串的长度很小,而且我们是知道识别字符串 $t$的, 那么首先要使得 $s$中不存在 $t$,我们可以直接假设 $s$ 全为 $0$, 用 $find$ 函数查看是否存在歧义,存在则用全 $1$ 代替。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
void solve(){
    int n, f = 0; string t; cin >> n >> t; string x = t;
    for(int i = 1; i <= n; ++ i) x = x + "0"; x = x + t;
    if(x.find(t, 1) != x.size() - t.size()) f = 1;
    for(int i = 1; i <= n; ++ i) cout << f; cout << '\n';
}
int main(){
    int T; cin >> T;
    while(T --) solve();
}

题J

题意:给定整数 $n$ 和 $m$,要求构造长度为 $n$ 的数组 $a$,其中 $-m<=a_i<=m$, 对任意 $2<=k<=n$,满足连续 $k$ 个元素和 $>=0$,问存在多少种构造情况$(1<=n,m<=5000, 答案模998244252)$

解:用 $dp[i][j]$ 表示前 $i$ 个元素种包含第 $i$ 个元素最小和为 $j$ 的情况数。我们再用一个 $sum[i][j]$ 表示包含第 $i$ 个元素和为 $j$ 的情况数,这样我们对和从大到小枚举,就 $O(n*m)$ 跑过。

#include<iostream>
using namespace std;
const int N = 5010, MOD = 998244353;
int dp[N][N + N], sum[N][N + N];
int main(){
    int n, m; cin >> n >> m;
    for(int i = m; i >= -m; -- i) {
        sum[1][i + N] = 1;
        dp[1][i + N] = dp[1][i + N + 1] + sum[1][i + N];
    }
    for(int i = 2; i <= n; ++ i){
        for(int j = m; j >= -m; -- j){
            if(j >= 0) sum[i][j + N] = dp[i - 1][j + N - m];
            else sum[i][j + N] = dp[i - 1][N - j];
            dp[i][j + N] = (dp[i][j + N + 1] + sum[i][j + N]) % MOD;
        }
    }
    cout << dp[n][N - m];
}