百度之星 (2021-08-01)

Published 2021-07-31 16:00 1548 words 8 min read Handwritten Every word here was typed by me.

Zhiheng Wang avatar

Zhiheng Wang

技术文章、个人随笔、项目与作品体验记录

This post is not yet available in English. Showing the original.
百度之星初赛第二场,记一下这几题。 A. 签到 题目链接 每次把 $(a,b)$ 同时变成 $(a+b,a-b)$。求做完 $k$ 次后的两个数,对 $998244353$ 取模。 连续变换两次就变成 $(2a,2b)$。先用快速幂算出 $2^{\lfloor k/2\rfloor}$,如果 $k$ 是奇数,再做一次变换就行。减法记得补一下模数。 B. 随机题意 题目链接 给定 $n$...

2021 百度之星初赛第二场,记一下这几题。

A. 签到

题目链接

题意

每次把 (a,b)(a,b) 同时变成 (a+b,a−b)(a+b,a-b)。求做完 kk 次后的两个数,对 998244353998244353 取模。

思路

连续变换两次就变成 (2a,2b)(2a,2b)。先用快速幂算出 2⌊k/2⌋2^{\lfloor k/2\rfloor},如果 kk 是奇数,再做一次变换就行。减法记得补一下模数。

代码

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

typedef long long ll;
const int mo = 998244353;


int T, a, b, k;

int fpow(int y, int x) {
    int res = 1; for(; x; x >>= 1, y = 1ll * y * y % mo) if(x & 1) res = 1ll * res * y % mo; return res;
}

int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d", &a, &b, &k);
        int tmp = k / 2;
        int f2 = fpow(2, tmp);
        // cout << f2 << endl;
        int A = 1ll * a * f2 % mo, B = 1ll * b * f2 % mo;
        if(k & 1) printf("%d %d\n", (A + B) % mo, (A - B + mo) % mo);
        else printf("%d %d\n", A, B);
    }
    return 0;
}

B. 随机题意

题目链接

题意

给定 nn 个整数,每个数都可以加上一个 [−k,k][-k,k] 内的整数。求修改后最多能有多少个不同的数。

思路

从小到大处理。一个数能放的位置是 [ai−k,ai+k][a_i-k,a_i+k],尽量占最靠左的空位置,给后面的数留空间。相同的数一起统计,直接算这一段还能塞下几个,不用一个个枚举。

代码

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

typedef long long ll;
const int maxn = 1e5 + 17;
const int oo = 0x3f3f3f3f;
int T, n, k, a[maxn], L, ans;
map<int, int> mp;
int main() {
    scanf("%d", &T);
    while(T--) {
        L = -oo;
        ans = 0;
        mp.clear();
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= n; i++) mp[a[i]]++;
        for(auto &it : mp) {
            int d = it.first, cnt = it.second;
            int l = d - k, r = d + k;
            l = max(l, L);
            r = min(r, l + cnt - 1);
            ans += max(0, r - l + 1);

            L = max(L, r + 1);

        }
        printf("%d\n", ans);
    }
    return 0;
}

C. 魔怔

题目链接

题意

完全无向图的边有黑白两种颜色。从 ss 出发,每经过一条边就把它的颜色翻转,要求最后所有边都变黑。求最少走多少条边,无解输出 −1-1。

思路

白边要走奇数次,黑边要走偶数次,所以先看白边图的欧拉路条件:奇度点只能有零个或两个;有两个时,起点必须是其中一个。

白边先各算一次。不同的白边连通块之间需要往返连接,每多接一个块加两步;起点如果不在任何白边块里,还要额外往返一次。用并查集数一下块数就行。

代码

/*
2
5 1
0
11
110
1100
4 1
0
00
000

2
5 1
1
11
110
1100
4 1
0
00
000
*/

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

const int maxn = 1e3 + 17;
int T, n, s;
int du[maxn];
int ans, fa[maxn], vis[maxn];
char str[maxn];

int getf(int x) {
    return fa[x] == x ? x : fa[x] = getf(fa[x]);
}

void merge(int x, int y) {
    int fx = getf(x), fy = getf(y);
    if(fx != fy) fa[fx] = fy;
}

int main() {
    scanf("%d", &T);
    while(T--) {
        memset(du, 0, sizeof(du));
        memset(vis, 0, sizeof(vis));
        ans = 0;
        int flg1 = 1, flg2 = 1;
        scanf("%d%d", &n, &s);
        for(int i = 1; i <= n; i++) fa[i] = i;
        for(int i = 2; i <= n; i++) {
            scanf("%s", str + 1);
            for(int j = 1; j < i; j++) {
                if(str[j] == '0') {
                    ans++;
                    merge(i, j);
                    du[i]++, du[j]++;
                }
                else du[i] += 2, du[j] += 2;
            }
        }
        ans -= 2;
        for(int i = 1; i <= n; i++) {
            if(getf(i) != i) {
                if(!vis[getf(i)]) ans += 2;
                vis[getf(i)] = 1;
            }
        }
        if(!vis[getf(s)]) ans += 2;
        for(int i = 1; i <= n; i++) if(du[i] & 1) flg1 = 0;
        for(int i = 1; i <= n; i++) {
            if(du[i] & 1 && i != s) {
                if(flg2 == 1) flg2++;
                else if(flg2 == 2) flg2 = 0;
            }
        }
        if(!(du[s] & 1)) flg2 = 0;
        if(flg1 || flg2) {
            printf("%d\n", ans);
        }
        else printf("-1\n");
    }
    return 0;
}

D. 净化

题目链接

题意

初始数值为 00,每轮依次加上 a1,…,ana_1,\ldots,a_n,每次加完如果小于 00 就变成 00。求最早在哪一轮能达到至少 mm,永远达不到就输出 −1-1。

思路

先把前两轮模拟出来,不能只看一轮结束时的值,中间也可能已经达到 mm。

设第一轮结束值为 xx,一轮总和为 SS。如果 S≤0S\le0,第二轮结束后就稳定了,再往后不会出现新值。如果 S>0S>0,从第二轮开始就不会再被截到零,每轮都比上一轮多 SS。枚举轮内前缀和 sis_i,算出 x+six+s_i 还差多少个 SS,取最小轮数。

代码

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

typedef long long ll;
const int maxn = 1e5 + 17;
const ll oo = 2e18;
int T, n, tot;
ll m, a[maxn], s[maxn], b[maxn];

inline void add(int x, ll k) {
	 for(; x <= n; x += x & -x) b[x] += k;
}

inline ll ask(int x) {
	ll res = 0; for(; x; x -= x & -x) res += b[x]; return res;
}

ll work() {
    memset(b, 0, sizeof(b));
    memset(s, 0, sizeof(s));
    for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
    for(int i = 1; i <= n; i++) b[i] = s[i] - s[i - (i & -i)];
    for(int i = 1; i <= n; i++) {
        ll tmp = ask(i);
        if(tmp >= m) return 1;
        if(tmp < 0) {
            add(i, -tmp);
        }
    }
    ll x = ask(n);
    ll res = oo;
    ll cur = x;
    for(int i = 1; i <= n; i++) {
        cur = max(0ll, cur + a[i]);
        if(cur >= m) return 2;
    }
    if(s[n] <= 0) return -1;
    for(int i = 1; i <= n; i++) {
        ll diff = m - x - s[i];
        if(diff <= 0) return 2;
        res = min(res, (diff - 1) / s[n] + 1);
    }
    if(res == oo) return -1;
    return res + 2;

    // tot = 0;
    // ll mx = 0;
    // int p = 1;
    // for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i], mx = max(s[i], mx);
    // for(int i = 1; i <= n; i++) if(s[i] < 0) v[++tot] = s[i];
    // sort(v + 1, v + 1 + tot, greater<int>());
    // ll t = 1, x = 0;
    // while(1) {
    //     for(int i = 1; i <= n; i++) b[i] = s[i] - s[i - (i & -i)];
    //     for(int i = 1; i <= n; i++) {
    //         ll tmp = ask(i);
    //         if(x + tmp == m) return t;
    //         if(x + tmp < 0) {
    //             add(i, -x - tmp);
    //         }
    //     }
    //     ll tmp = ask(n);
    //     if(tmp == 0) return -1;
    //     x += tmp, t++;
    //     while(v[p] + x >= 0 && p <= tot) p++;
    //     if(p > tot) {
    //         ll diff = m - x - mx;
    //         t += (diff / s[n]);
    //         if(diff % s[n]) t++;
    //         return t;
    //     }
    //     ll diff = 0 - (x + v[p]);
    //     t += (diff / tmp), x += (diff / tmp) * tmp;
    // }

}

int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d%lld", &n, &m);
        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        printf("%lld\n", work());
    }
    return 0;
}

Written by Handwritten

Random Posts

© 2026 Zhiheng Wang @traceonsnow
Powered by theme astro-koharu · Inspired by Shoka
Loading...