2024牛客寒假算法基础训练营4

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

注:本题解只涉及赛时思路和赛后补题收获,人菜佬勿喷

赛时六题【ABCDEG】补题【FHIK】

J是个状压dp,没搞懂,暂时未补......

A.柠檬可乐

A-柠檬可乐_2024牛客寒假算法基础集训营4 (nowcoder.com)

一天,阿宁在街道看到有柠檬可乐卖,于是点了一杯。柠檬切片,倒可乐,摇晃一下,阿宁对于这么简单的步骤感到惊讶......

阿宁买的这杯柠檬可乐的甜度是a,酸度是 b 。如果 a≥k×b,那么阿宁就觉得好喝,否则阿宁就觉得亏了。

1≤a,b,k≤100

分析:

签到题,判断 a 与 b×k 的大小即可

代码:

void solve(){
    int a, b, k;
    cin >> a >> b >> k;
    if(a >= k * b)
        cout << "good";
    else
        cout << "bad";
}

B.左右互博

B-左右互博_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

每堆石子最多可以操作 a_i −1 次,所以只需要统计 a_i−1 的总和,判断奇偶性即可

代码:

void solve(){
    int n;
    cin >> n;
    vector<int>a(n);
    int sum = 0;
    for(int i = 0; i < n; i++)
        cin >> a[i], sum += a[i] - 1;
    if(sum % 2 == 0)
        cout << "sweet";
    else
        cout << "gui";
}

C.冬眠

C-冬眠_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

这个题的数据范围都很小,直接暴力模拟即可

代码:

void solve(){
    int n, m, x, y;
    cin >> n >> m >> x >> y;
    char s[105][105];
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> s[i][j];
    char a = s[x][y];
    int p, q;
    cin >> p >> q;
    int op[105], z[105];
    for(int i = 1; i <= q; i++){
        cin >> op[i] >> z[i];
    }
    for(int k = 0; k < p; k++){
        for(int j = 1; j <= q; j++){
            if(op[j] == 1){
                char r = s[z[j]][m], t;
                for(int i = 1; i < m; i++){
                    t = s[z[j]][i];
                    s[z[j]][i] = r;
                    r = t;
                }
                s[z[j]][m] = r;
            }
            else{
                char r = s[n][z[j]], t;
                for(int i = 1; i < n; i++){
                    t = s[i][z[j]];
                    s[i][z[j]] = r;
                    r = t;
                }
                s[n][z[j]] = r;
            }
        }
        
    }
    cout << s[x][y];
}

D.守恒

D-守恒_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

题目名字很关键,守恒。读题可以发现数组的总和是定值,所以只需要统计总和的因数,且这个因数大于n即可,感觉这道题跟cf前几天的一道题很像,那道题是筛素因子,这道题只需筛大于n的因数即可

代码:

(最后一个for循环的i开的int类型 结束条件写的 i∗n<=sum 结果爆掉了 超时了三发,哭死

void solve(){
    ll n;
    cin >> n;
    vector<ll>a(n);
    ll sum = 0;
    for(ll i = 0; i < n; i++){
        cin >> a[i];
        sum += a[i];
    }
    if(n == 1){
        cout << 1;
        return;
    }
    ll ans = 0;
    for(ll i = 1; i <= sum / n; i++){
        if(sum % i == 0 && sum / i >= n)
            ans++;
    }
    cout << ans;
}

E.漂亮数组

E-漂亮数组_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

读完题感觉好像是dp,但是有将近1000发通过,恐怖如斯。

仔细一想,这个题只需要对区间和 %k 的值进行记录判断即可

如果两个区间和 %k 的值一样,那说明这两个区间相减即是漂亮数组( %k 的值相减之后变成0了)

如果一个区间和本身 %k 就是0,那说明这个区间也是个漂亮数组,直接统计即可

注意数据范围,记得开 longlong

代码:

void solve(){
    ll n, k;
    cin >> n >> k;
    vector<ll>a(n + 1);
    map<ll, ll> mp;
    ll ans = 0;
    ll sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
        mp[sum % k]++;
        if(sum % k == 0 || mp[sum % k] >= 2){
            ans++;
            mp.clear();
            sum = 0;
        }
    }
    cout << ans << endl;
}

G.数三角形(easy)

G-数三角形(easy)_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

数据范围不是很大,用 dfs搜索即可,从"*"出发,假设当前坐标为dx, dy,那么(dx + 1,dy - 1)和(dx + 1, dy + 1)一定也是 "*",同时(dx + 1, dy)也应该是“*”,此时是高为1的情况,高为其他情况同理,使用 dfs 递归求解即可,但是太暴力的写法也会tle,所以提前求一个前缀和数组,用来记录每一行的“*”个数,这样在判断底边是否符合题意是便可以以O(1)复杂度查询。

代码:

void solve(){
    int n, m;
    cin >> n >> m;
    char a[505][505];
    int b[505][505];
    for(int i = n; i >= 1; i--){
        string s;
        cin >> s;
        for(int j = 1; j <= m; j++)
            a[i][j] = s[j - 1]; 
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            b[i][j] = b[i][j - 1] + (a[i][j] == '*' ? 1 : 0);//前缀和数组
    int ans = 0;
    auto dfs = [&](auto self, int fx, int op, int po) -> void{
        if(fx <= 0 || op <= 0 || po > m)
            return;
        if(a[fx][op] != '*' || a[fx][po] != '*')
            return;
        if(b[fx][po] - b[fx][op - 1] == po - op + 1)
            ans++;
        self(self, fx - 1, op - 1, po + 1);
    };
    for(int i = 2; i <= n; i++){
        for(int j = 2; j <= m - 1; j++){
            if(a[i][j] == '*'){
                dfs(dfs, i - 1, j - 1, j + 1);
            }
        }
    }
    cout << ans;
}

F.来点每日一题

F-来点每日一题_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

读完题发现很浓的dp味道,分析得分式子:

((b1−b2)×b3−b4)×b5−b6

要想值越大,那么必须前面的式子要越大,对于乘法运算来说,会出现两种情况

正数 × 正数 = 正数

负数 × 负数 = 正数

所以我们只需维护区间的最大最小值即可

定义函数maxn[i][j]代表区间从第i个物品到第j个物品之间取东西的最大价值。f[i][j]意味着取到i(1~6)个数字,即体积为i时,j = 0是最大价值,j = 1是最小价值

再定义一个 dp[i] 表示在前 i 个数,以第i个数为结尾并且这个区间选满了6个数的最大分数

最后 dp[n] 即为答案

代码:

ll dp[N], a[N], f[N][2], maxn[N][N], INF = 1e18;
void solve(){
    ll n;
    cin >> n;
    for(int i = 1; i <= n ;i++) 
        cin >> a[i];
    if(n < 6){
        cout << "0" << endl;
        return;
    }
    for(int i = 1; i <= n ;i++){
        for(int j = 1; j <= 6; j++){
            f[j][0] = -INF;
            f[j][1] = INF;
        } 
        for(int j = i; j <= n; j++){
            for(int k = 6; k >= 1; k--){
                if(k == 1){
                    f[k][0] = max(f[k][0], f[k - 1][0] + a[j]);
                    f[k][1] = min(f[k][1], f[k - 1][1] + a[j]);
                }
                else if(k % 2 == 0){
                    f[k][0] = max(f[k][0], f[k - 1][0] - a[j]);
                    f[k][1] = min(f[k][1], f[k - 1][1] - a[j]);
                }
                else{
                    for(int z = 0; z < 2; z++){
                        if(abs(f[k - 1][z]) > INF / 2) 
                            continue;
                        f[k][0] = max(f[k][0], f[k - 1][z] * a[j]);
                        f[k][1] = min(f[k][1], f[k - 1][z] * a[j]);
                    }
                }
            }
            maxn[i][j] = max(maxn[i][j], f[6][0]);
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = i - 1; j >= 0; j--){
            dp[i] = max(dp[i], dp[j] + maxn[j + 1][i]);
        }
    }
    cout << dp[n] << endl;
}

H.数三角形(hard)

H-数三角形(hard)_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

与 G 题不同的是,这题的数据范围大了许多,不能再用暴力求解,基于 G题前缀和优化的思路,不难想到用树状数组进行优化,用树状数组来记录每个点下方的贡献,在统计前进行预处理即可

代码:

template <class T>
struct szarray{
    int n;
    vector<T> a;
    szarray(int n = 0) : n(n), a(n, T()) {}
    void modify(int i, T x) {
        for (i++; i <= n; i += i & -i) {
            a[i - 1] += x;
        }
    }
    T get(int i) {
        T res = T();
        for (; i > 0; i -= i & -i) {
            res += a[i - 1];
        }
        return res;
    }
    T sum(int l, int r) {
        return get(r) - get(l);
    }
};
void solve(){
    int n, m;
    cin >> n >> m;
    vector<string>a(n);
    vector l(n + 1, vector<int>(m + 2)), r(n + 1, vector<int>(m + 2));
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        for (int j = 0; j < m; j++) {
            if (a[i][j] == '*') {
                l[i + 1][j + 1] = l[i][j] + 1;
                r[i + 1][j + 1] = r[i][j + 2] + 1;
            }
        }
    }
    vector z(2, szarray<int>(m));
    ll ans = 0;
    for (int i = 1; i < n; i++) {
        vector<int> num(m + 1);
        for (int j = m - 1; j >= 0; j--) {
            if (a[i][j] == '*') {
                num[j] = num[j + 1] + 1;
            }
        }
        vector<vector<int>> sc(m);
        for (int j = 0; j < m; j++) {
            if (a[i][j] == '*') {
                ans += z[j % 2].sum(j - l[i + 1][j + 1] * 2, m);
                z[j % 2].modify(j, +1);

                sc[min(j + num[j] - 1, j + r[i + 1][j + 1] * 2 - 2)].push_back(j);
                for (auto k : sc[j]) {
                    z[k % 2].modify(k, -1);
                }
            }
        }
    }
    cout << ans << endl;
}

I.回头

I-回头_2024牛客寒假算法基础集训营4 (nowcoder.com)

分析:

这个题是个很明显的最短路问题,跑一下dij,再记录一下是否被修改,得到最短路径即可

代码:

void solve(){
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, int>>> adj(n);
    vector<array<ll, 2>> f(n, {INF, INF});
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        u--, v--;
        adj[u].emplace_back(v, w);
        if (w < f[u][0]) {
            f[u][1] = f[u][0];
            f[u][0] = w;
        } else if (w < f[u][1]) {
            f[u][1] = w;
        }
    }
    vector<array<ll, 2>> dis(n, {INF, INF});
    priority_queue<tuple<ll, int, int>, vector<tuple<ll, int, int>>, greater<>>q;
    q.emplace(0LL, 0, 0);
    ll ans = INF;
    while (!q.empty()) {
        auto [d, x, t] = q.top();
        q.pop();
        if (dis[x][t] != INF){
            continue;
        }
        dis[x][t] = d;
        if (adj[x].size() > t){
            for (auto [y, w] : adj[x]){
                q.emplace(d + w + (t ? f[x][w == f[x][0]] : 0), y, 0);
                q.emplace(d + (t ? f[x][w == f[x][0]] : 0), y, 1);
            }
        }
    }
    for (int i = 0; i < 2 && i <= adj[n - 1].size(); i++){
        ans = min(ans, dis[n - 1][i] + (i == 0 ? 0 : f[n - 1][0]));
    }
    if (ans == INF) {
        ans = -1;
    }
    cout << ans << endl;
}

K.方块掉落

K-方块掉落_2024牛客寒假算法基础集训营4 (nowcoder.com)

最近阿宁对一个名叫“方块掉落”的游戏感兴趣,沉迷于此。

每局游戏一开始,有一条无限长的水平线、一个箭头、一个操作序列 t ,没有任何方块。

箭头位水平线的下方,代表方块掉落的位置。

操作序列 t 是一个字符串,仅包含 "YBR" 三种字符,分别代表颜色黄蓝红。依次按照操作序列 s 掉落不同颜色的方块。

如果即将掉落的是黄色方块,那么箭头指向的位置掉落一个黄色方块。例如:

如果即将掉落的是蓝色方块,首先箭头移到下一个位置;然后箭头指向的位置掉落一个蓝色方块。例如:

如果即将掉落的是红色方块,在箭头指向的那一列,掉落这一列所有存在的方块;然后箭头指向的位置掉落一个红色方块。例如:

分析:

嗯,单点修改,区间查询,线段树无疑了

分析题目发现,我们只需要用ls与rs维护一段区域中, 最左端区间与最右端区间的方块高度,用lz记录最左与最右端的红色方块数

在 pushup 函数中,根据当前的颜色采取不同的修改即可

代码:

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using db = double;
#define endl "\n"
#define rep(i, a, n) for(int i = a; i < n; i++)
#define per(i, a, n) for(int i = a; i >= n; i--)
#define all(x) x.begin(), x.end()
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const double PI = acos(-1);
struct cmp{
    template <typename T, typename U>
    bool operator()(T const &left, U const &right)
    {
        return left.first > right.first;
    }
};
const int Y = 'Y';
const int R = 'R';
const int B = 'B';
struct segtree{
    ll l, r;
    ll lz;
    ll ls, rs;
    ll res;
    ll ok;
    segtree(){
        ls = rs = 0;
        l = r = 0;
        lz = 0;
        res = 0;
        ok = 0;
    }
}tr[N << 2];
ll a[N];
void pushup(segtree &u, segtree &x, segtree &y){
    u.ok = 1;
	u.lz = 0;
	if (!x.ok)
	{
		u.res = y.res;
		u.rs = y.rs;
		u.ls = y.ls;
		u.lz = y.lz;
	}
	else if (!y.ok)
	{
		u.res = x.res;
		u.rs = x.rs;
		u.ls = x.ls;
		u.lz = x.lz;
	}
	else
	{
		u.res = (x.res + y.res) % mod;
		u.ls = x.ls;
		u.rs = y.rs;
		if (x.ls == x.res) u.ls += y.ls, u.ls % mod;
		if (y.rs == y.res and y.ls) u.rs += x.rs, u.rs %= mod;
		if (x.lz) u.lz += x.lz;
		if (y.lz)
		{
			u.res = (y.res + x.res + x.rs * y.lz) % mod;
			if (x.ls == x.res) u.lz += y.lz + (y.lz) * x.lz, u.lz %= mod, u.ls += x.res * y.lz, u.ls %= mod;
			if (y.rs == y.res) u.rs += x.rs * y.lz, u.rs %= mod;
		}
	}
}

void pushup(ll u){
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(ll u, ll l, ll r){
    tr[u].l = l;
    tr[u].r = r;
    if(l == r){
        tr[u].ok = 1;
        tr[u].res = 1;
        if(a[l] == Y){
            tr[u].ls = 1;
            tr[u].rs = 1;
        }
        if(a[l] == B){
            tr[u].ls = 0;
            tr[u].rs = 1;
        }
        if(a[l] == R){
            tr[u].lz = 1;
            tr[u].ls = 1;
            tr[u].rs = 1;
        }
        return;
    }
    ll mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}
void modify(ll u, ll l, ll r, ll x){
    if(tr[u].l >= l && tr[u].r <= r){
        if(x == Y){
            tr[u].lz = 0;
            tr[u].ls = 1;
            tr[u].rs = 1;
        }
        if(x == B){
            tr[u].lz = 0;
            tr[u].ls = 0;
            tr[u].rs = 1;
        }
        if(x == R){
            tr[u].lz = 1;
            tr[u].ls = 1;
            tr[u].rs = 1;
        }
        return;
    }
    if (tr[u << 1].r >= l)
		modify(u << 1, l, r, x);
	if (tr[u << 1 | 1].l <= r)
		modify(u << 1 | 1, l, r, x);
    pushup(u);
}
segtree query(ll u, ll l, ll r){
    if(tr[u].l >= l && tr[u].r <= r){
        return tr[u];
    }
    segtree res, tmp1, tmp2;
	if (tr[u << 1].r >= l) tmp1 = query(u << 1, l, r);
	if (tr[u << 1 | 1].l <= r) tmp2 = query(u << 1 | 1, l, r);
	pushup(res, tmp1, tmp2);
    return res;
}
void solve(){
    ll n, q;
    cin >> n >> q;
    string s;
    cin >> s;
    for(int i = 1; i <= n; i++){
        a[i] = s[i - 1];
    }
    build(1, 1, n);
    while(q--){
        ll op;
        cin >> op;
        if(op == 1){
            ll p;
            char ce;
            cin >> p >> ce;
            ll k = ce;
            modify(1, p, p, k);
        }
        else{
            ll ii, ll;
            cin >> ii >> ll;
            cout << query(1, ii, ll).res << endl;
        }
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int _;
    _ = 1;
    //cin >> _;
    while (_--) {
        solve();
    }
    return 0;
}
  • 50
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值