2023 SZTU-ACM迎新赛

写在前面

难度题号考察点
签到\(I\ M\)基本的语言知识
简单\(L\ B\ G\ E\)枚举、贪心等基础算法思想
中等\(D\ F\ K\ H\)基础算法和代码实现能力
困难\(J\ A\ C\)基础算法的进阶

预估:冠军 \(8\) 题,金牌 \(5\) 题,银牌 \(4\)

A 要迟到了!

考点:

  • 搜索
  • 图论

题解:

困难题

解法一:两遍 \(BFS\) \(O(n*m + k)\)

从起点开始做 \(BFS\) 记录从起点到每个位置只走路(每步时间为 \(t_1\) )的时间 \(t_{w}\)

从终点开始做 \(BFS\) 记录从终点到每个位置用单车(每步时间为 \(t_2\) )的时间 \(t_c\)

维护一个最小值 \(ans\)

遍历所有的单车的位置 \((i,j)\) ,此时 \(ans = min(ans,\ t_w[i][j] + t_c[i][j])\)

再额外判断一次如果不找单车,直接用走路的方式从起点到终点 \((i,j)\) ,即 \(ans = min(ans,\ t_w[i][j])\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;

int n, m, t1, t2;
pair<int, int> S, T;
vector<pair<int, int>> C;
char mp[N][N];
vector<vector<int>> t_w(N, vector<int>(N, -1));
vector<vector<int>> t_c(N, vector<int>(N, -1));
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
bool vis[N][N];

void bfs(pair<int, int> PII, vector<vector<int>> &t, int dt)
{
    memset(vis, 0, sizeof(vis));
    queue<pair<int, int>> q;

    vis[PII.first][PII.second] = true;
    t[PII.first][PII.second] = 0;
    q.push(make_pair(PII.first, PII.second));
    while (!q.empty())
    {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();

        for (int i = 0; i < 4; i++)
        {
            int tx = x + dx[i];
            int ty = y + dy[i];

            if (tx < 1 || ty < 1 || tx > n || ty > m || vis[tx][ty] || mp[tx][ty] == '#')
                continue;

            vis[tx][ty] = true;
            t[tx][ty] = t[x][y] + dt;
            q.push(make_pair(tx, ty));
        }
    }
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    cin >> n >> m >> t1 >> t2;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> mp[i][j];
            if (mp[i][j] == 'S')
                S = make_pair(i, j);
            else if (mp[i][j] == 'T')
                T = make_pair(i, j);
            else if (mp[i][j] == 'C')
                C.push_back(make_pair(i, j));
        }
    }

    bfs(S, t_w, t1);
    bfs(T, t_c, t2);

    int ans = 0x3f3f3f3f;
    for (auto it : C)
    {
        int cx = it.first;
        int cy = it.second;
        ans = min(ans, t_w[cx][cy] + t_c[cx][cy]);
    }
    ans = min(ans, t_w[T.first][T.second]);

    cout << ans;

    return 0;
}

解法二:BFS+优先队列 \(O(2nm \log (nm))\)

使用优先队列让最短时间在队首,时间相同的时候优先有车的。

\(BFS\) 直到到达终点,此时的时间即为答案。若队列结束也没有到达终点则返回 \(-1\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;

int n, m, t1, t2;
int sx, sy;
char mp[N][N];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
bool vis[N][N][2];

struct node
{
    int x, y, t;
    bool flag;
    friend bool operator<(const node a, const node b)
    {
        if (a.t != b.t)
            return a.t > b.t;
        return a.flag < b.flag;
    }
};

int bfs()
{
    priority_queue<node> q;

    q.push({sx, sy, 0, 0});
    vis[sx][sy][0] = true;
    while (!q.empty())
    {
        node now = q.top();
        int x = now.x;
        int y = now.y;
        q.pop();

        if (mp[x][y] == 'T')
            return now.t;

        for (int i = 0; i < 4; i++)
        {
            int tx = x + dx[i];
            int ty = y + dy[i];
            int tt = now.t + (now.flag ? t2 : t1);
            bool flag = now.flag || mp[tx][ty] == 'C';

            if (tx < 1 || ty < 1 || tx > n || ty > m || vis[tx][ty][flag] || mp[tx][ty] == '#')
                continue;

            vis[tx][ty][flag] = true;
            q.push({tx, ty, tt, flag});
        }
    }
    return -1;
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    cin >> n >> m >> t1 >> t2;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> mp[i][j];
            if (mp[i][j] == 'S')
                sx = i, sy = j;
        }
    }

    cout << bfs();

    return 0;
}

B 打怪兽

考点:

  • 遍历

题解:

简单题

遍历所有的怪兽,求出每个怪兽对任何一个小蔡的最短距离,输出其中的最大值即可。

复杂度: \(O(n*k)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
using namespace std;

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n, k;
    cin >> n >> k;

    vector<int> v(k + 10);
    for (int i = 1; i <= k; i++)
        cin >> v[i];

    vector<pair<int, int>> a(n + 10);
    for (int i = 1; i <= n; i++)
        cin >> a[i].first >> a[i].second;

    double ans = 0;
    for (int i = 1; i <= n; i++)
    {
        int x = a[i].first;
        int y = a[i].second;
        double t = 1e18;
        for (int j = 1; j <= k; j++)
        {
            int xx = a[v[j]].first;
            int yy = a[v[j]].second;
            t = min(t, sqrt(pow(x - xx, 2) + pow(y - yy, 2)));
        }
        ans = max(ans, t);
    }

    cout << fixed << setprecision(10) << ans;

    return 0;
}

C 演唱会

考点:

  • 动态规划

题解:

困难题

定义了一个结构体 node ,其中包含了每一天的信息,包括慧慧可以完成的任务数量 \(l\) ,完成一项任务的薪资 \(q\) ,以及这一天是哪一天 \(s\) 。此外,我们还计算了这一天可以完成任务的时间段 \(ledge\)\(redge\)

定义了一个二维数组 dp ,其中 dp[i][j] 表示在第 \(i\) 天结束时,如果慧慧选择最后提前做到第 \(j\) 天工作,她可以获得的最大收入。

对于每一天,我们有两种选择:要么选择工作,要么选择休息。如果选择工作,我们需要更新 dp[i][j] 的值。为了找到最优的工作日期,我们使用了一个优先队列 que 来存储可能的工作日期。在每一天结束时,我们检查队列中的任务是否可以在当前日期完成,如果可以,我们就更新 dp[i][j] 的值。

优先队列 que 的作用是帮助我们在 \(O(1)\) 的时间复杂度内找到最优的工作日期。具体来说,优先队列中存储的是一对值,第一个值是 \(dp[i - 1][j] - j * day[i].q\) ,表示如果在第 \(j\) 天工作,那么慧慧可以获得的收入;第二个值是 \(j\) ,表示这一天是哪一天。

在每一天结束时,我们检查队列中的任务是否可以在当前日期完成,如果可以,我们就更新 dp[i][j] 的值。为了实现这一点,我们在每一天开始时,都会将可能的工作日期添加到优先队列中。然后,在每一天结束时,我们检查队列的顶部元素,如果这一天的任务可以在当前日期完成,那么我们就从队列中弹出这个元素,并更新 dp[i][j] 的值。

最后,我们遍历 dp[k] 数组,找到最大的收入,这就是慧慧可以获得的最大收入。

复杂度: \(O(n*k)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
using namespace std;
const int N = 16000 + 10;

struct node
{
    int s, l, q;
    int ledge, redge;
    friend bool operator<(const node &a, const node &b)
    {
        return a.s < b.s;
    }
} day[105];

int n, k;
int dp[105][N];

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= k; i++)
    {
        cin >> day[i].l >> day[i].q >> day[i].s;
        day[i].ledge = max(0, day[i].s - day[i].l);
        day[i].redge = min(n, day[i].s + day[i].l - 1);
    }

    sort(day + 1, day + 1 + k);

    for (int i = 1; i <= k; i++)
    {
        for (int j = 1; j < day[i].s; j++)
            dp[i][j] = dp[i - 1][j];

        priority_queue<pair<int, int>> que;
        for (int j = day[i].ledge; j < day[i].s; j++)
        {
            int cur = dp[i - 1][j] - j * day[i].q;
            while (!que.empty() && dp[i - 1][que.top().second] - que.top().second * day[i].q <= cur)
                que.pop();
            que.push({cur, j});
        }
        for (int j = day[i].s; j <= day[i].redge; j++)
        {
            while (!que.empty() && j - que.top().second > day[i].l)
                que.pop();
            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            dp[i][j] = max(dp[i][j], dp[i - 1][que.top().second] + (j - que.top().second) * day[i].q);
        }
        for (int j = day[i].redge + 1; j <= n; j++)
        {
            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }

    int ans = 0;
    for (int i = 1; i <= n; i++)
        ans = max(ans, dp[k][i]);

    cout << ans << endl;

    return 0;
}

D 总要赢一次吧

考点:

  • \(bash\) 博弈

题解:

中等题

由题可知,本题需要找先手必胜的条件。

对于总量为 \(v\) 升的酒,如果每人每次只能喝 \(1 \sim k\) 升的酒。想赢就需要让对方喝完后还有剩余,并且在 \(1 \sim k\) 升之间。所以只要让酒一直是 \(n(k+1)\ (n \in N)\) 升就一定能够赢。

证明:当酒为 \(n(k+1)\ (n \in N)\) 时,如果对方喝 \(a\) 升,我们一定能找到 \(b\) 满足 \(a+b=k+1\) ,重复此过程,直到还有 \(k+1\) 升酒。此时不论对方喝多少,我们都可以喝最后一次。

故我们只需要找到满足 \(v\%(k+1)\ne 0\) 的情况,此时先手必胜,找出最大的k即可。

复杂度: \(O(n*m)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <bits/stdc++.h>
using namespace std;

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n, cnt = 0;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        int v, m, k, ans = -1;
        cin >> v >> m;
        while (m--)
        {
            cin >> k;
            if (v % (k + 1))
                ans = max(ans, k);
        }
        if (ans != -1)
            cnt++;

        cout << ans << '\n';
    }
    cout << (cnt > n - cnt ? "successful" : "I can't help you");

    return 0;
}

E 臭袜子

考点:

  • STL

题解:

简单题

使用 map 嵌套 pair 进行编号的计数,答案更新的情况有两种,一种是出现的次数比现在的更多,一种是次数相同但是第一次出现的更早。

复杂度: \(O(n)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>
using namespace std;

map<pair<int, int>, int> cnt;
map<pair<int, int>, int> t;
pair<int, int> ans;

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        pair<int, int> now;
        cin >> now.first >> now.second;
        cnt[now]++;

        if (t.count(now) == 0)
            t[now] = i;

        if (cnt[now] > cnt[ans] || cnt[now] == cnt[ans] && t[now] <= t[ans])
            ans = now;
    }
    cout << cnt[ans] << ' ' << ans.first << ' ' << ans.second;

    return 0;
}

F 我要上厕所!

考点:

  • 模拟
  • STL
  • 分治

题解:

中等题

解法一:分治 \(O(n\log n)\)

分:将数组分成两半,直到只有一个元素

治:当只有一个元素的时候这个部分的最大就是它本身

合:将两个数组和在一起的时候是哪种情况:

  1. 最大子数组是其左侧数组的最大子数组
  2. 最大子数组是其右侧数组的最大子数组
  3. 最大子数组出现在中间位置:从中间向两侧分别加起来,找到分别的最大值然后求和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;

int findmid(vector<int> &a, int l, int mid, int r)
{
    int left_max = 0;
    int left_sum = 0;
    for (int i = mid; i >= l; i--)
    {
        left_sum += a[i];
        left_max = max(left_max, left_sum);
    }

    int right_max = 0;
    int right_sum = 0;
    for (int i = mid + 1; i <= r; i++)
    {
        right_sum += a[i];
        right_max = max(right_max, right_sum);
    }

    return left_max + right_max;
}

int find(vector<int> &a, int l, int r)
{
    if (l == r)
        return a[l];

    int mid = (l + r) / 2;

    int left_max = find(a, l, mid);
    int right_max = find(a, mid + 1, r);
    int mid_max = findmid(a, l, mid, r);

    return max({mid_max, left_max, right_max});
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n;
    cin >> n;

    map<string, bool> out;
    vector<int> a(N), b(N);

    for (int i = 0; i < n; i++)
    {
        int k, f, t;
        string s;
        cin >> k >> f >> s >> t;

        if ((k == 1 && s[0] > 'm') || (k == 2 && s[0] < 'n'))
            continue;

        if (f == 2)
        {
            out[s] = false;
            if (k == 1)
                a[t]--;
            else
                b[t]--;
        }
        else
        {
            if (out[s])
                continue;
            out[s] = true;
            if (k == 1)
                a[t]++;
            else
                b[t]++;
        }
    }

    cout << find(a, 0, 50000) << ' ' << find(b, 0, 50000);

    return 0;
}

解法二:dp \(O(n)\)

这里对 \(dp\) 数组的定义为 以为第 \(i\) 秒为结尾的最大子数组和为 dp[i]

状态转移方程为 \(dp[i] = max(nums[i],\ dp[i - 1] + nums[i])\) ,即如果加上第 \(i\) 秒更优就加,否则重新开始一段子数组。

注意到 dp[i] 仅仅和 dp[i-1] 的状态有关,所以可以直接在原数组操作。

\(nums[i] = max(nums[i],\ nums[i - 1] + nums[i])\)

输出其中的最大值即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;

int dp(vector<int> &a)
{
    int res = a[0];
    for (int i = 1; i <= 50000; i++)
    {
        a[i] = max(a[i - 1] + a[i], a[i]);
        res = max(res, a[i]);
    }
    return res;
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n;
    cin >> n;

    map<string, bool> out;
    vector<int> a(N), b(N);

    for (int i = 0; i < n; i++)
    {
        int k, f, t;
        string s;
        cin >> k >> f >> s >> t;

        if ((k == 1 && s[0] > 'm') || (k == 2 && s[0] < 'n'))
            continue;

        if (f == 2)
        {
            out[s] = false;
            if (k == 1)
                a[t]--;
            else
                b[t]--;
        }
        else
        {
            if (out[s])
                continue;
            out[s] = true;
            if (k == 1)
                a[t]++;
            else
                b[t]++;
        }
    }

    cout << dp(a) << ' ' << dp(b);

    return 0;
}

G Faker的阿卡丽

考点:

  • 贪心

题解:

简单题

我们可以一开始就使用 \(w\) ,增加 \(b\) 点能量和 \(1\) 点帅气值

然后我们将使用技能和普通攻击算作一个整体,即每次消耗 \(min(a,c)-d\) 点能量,增加 \(2\) 点帅气值

但是这样我们需要先把最后一次处理,因为此时的能量并不够我们先用再加,直接记为增加 \(2\) 点帅气值即可。

复杂度: \(O(1)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits/stdc++.h>
using namespace std;

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n, a, b, c, d;
    cin >> n >> a >> b >> c >> d;

    n += b;

    int t = min(a, c);
    n -= t;

    cout << 1 + n / (t - d) * 2LL + 2;

    return 0;
}

H 挑区间

考点:

  • 前缀和

题解:

中等题

对于一段区间 \([l,r]\) \((1 \le l \le r \le n)\) 我们可以预处理出前缀和数组 pre 和后缀和数组 sufpre[i] 表示这 \([1,i]\) 之间的累加和, suf[i] 表示 \([i,n]\) 之间的累加和。

我们可以把序列分为三段 \([1,l-1],[l,r],[r+1,n]\) 。其中 \([1,l-1]\)\([r+1,1]\) 我们通过前后缀和查询复杂度为 \(O(1)\)

对于 \([l,r]\) 我们枚举每一个左端点 \(l\) ,遍历所有的可能区间 \([l,r]\) ,复杂度为 \(O(n^2)\) 。需要注意的是边界的情况需要特判。

复杂度: \(O(n^2)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n, d;
    cin >> n >> d;
    vector<int> a(n + 10), b(n + 10);
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    vector<ll> pre(n + 10), suf(n + 10);
    for (int i = 2; i <= n; i++)
        pre[i] = pre[i - 1] + abs(a[i] - a[i - 1]);
    for (int i = n - 1; i >= 1; i--)
        suf[i] = suf[i + 1] + abs(a[i] - a[i + 1]);

    ll ans = 0;
    for (int l = 1; l <= n; l++)
    {
        b = a;
        ll tmp = 0;
        for (int r = l; r <= min(l + d - 1, n); r++)
        {
            b[r] = r;
            if (r != 1)
                tmp += abs(b[r] - b[r - 1]);
            if (r != n)
                tmp += abs(b[r] - b[r + 1]);
            ans = max(ans, pre[l - 1] + suf[r + 1] + tmp);
            if (r != n)
                tmp -= abs(b[r] - b[r + 1]);
        }
    }
    cout << ans;

    return 0;
}

I 四季

考点:

  • 基础语法

题解:

签到题

顺带考察常识:

  • \(3,4,5\) 是春天
  • \(6,7,8\) 是夏天
  • \(9,10,11\) 是秋天
  • \(12,1,2\) 是冬天
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    cin >> n;

    if (n <= 2)
        cout << "winter";
    else if (n <= 5)
        cout << "spring";
    else if (n <= 8)
        cout << "summer";
    else if (n <= 11)
        cout << "autumn";
    else
        cout << "winter";

    return 0;
}

J 飞升

考点:

  • 二分

题解:

困难题

对每个人的能力值进行二分,找到满足条件的最大值

二分边界为 \([a[i],a[i]+k]\) ,即一次都不用和 \(k\) 次都用在他身上

对于 check() 函数,从当前这位开始,向右边累加构造出阶梯型需要多少次操作。判断需要的次数 \(cnt\) 是否满足 \(cnt\le k\) 即可

复杂度: \(O(n log n)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;

vector<int> a(N);
int n, k;

bool check(int x, int i)
{
    ll cnt = 0;
    for (int p = 0; i + p <= n; p++)
    {
        if (a[i + p] >= (x - p))
        {
            if (cnt > k)
                return false;
            else
                return true;
        }

        cnt += (x - p) - a[i + p];
    }
    return false;
}

void solve()
{
    cin >> n >> k;

    int ans = 0;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int i = 1; i <= n; i++)
    {
        int l = a[i], r = a[i] + k, tmp;
        while (l <= r)
        {
            int mid = (l + r) / 2;
            if (check(mid, i))
                tmp = mid, l = mid + 1;
            else
                r = mid - 1;
        }
        ans = max(ans, tmp);
    }

    cout << ans << '\n';
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int t;
    cin >> t;
    while (t--)
        solve();

    return 0;
}

K f(x) = floor(log₂(x))

考点:

  • 数学

题解:

中等题

先预处理 \(10^{18}\) 内所有 \(2\) 的指数幂

然后一段一段的累加得到 \(1 \sim l\) 的和,同理得到 \(1 \sim r\) 的和

做差即可得到结果

复杂度: \(O(q \log r)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;

ll v[100] = {1};

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    for (int i = 1; i < 63; i++)
        v[i] = v[i - 1] * 2;

    int q;
    cin >> q;
    while (q--)
    {
        ll l, r;
        cin >> l >> r;

        ll a = 0, b = 0, p;

        p = upper_bound(v, v + 63, l) - v - 1;
        for (int i = 1; i < p; i++)
        {
            a += ((v[i] % mod) * i) % mod;
            a %= mod;
        }
        a += p * (l - v[p]) % mod;
        a %= mod;

        p = upper_bound(v, v + 63, r) - v - 1;
        for (int i = 1; i < p; i++)
        {
            b += ((v[i] % mod) * i) % mod;
            b %= mod;
        }
        b += p * ((r - v[p] + 1) % mod);
        b %= mod;

        cout << (b - a + mod) % mod << '\n';
    }

    return 0;
}

L 哪个倒霉蛋还得再体测一次

考点:

  • 递推

题解:

简单题

解法一:递推

易知: \(a[n]= \sum_{i=1}^{n/2}a[i]+1\)

复杂度: \(O(n^2)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;

int a[1010];

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= i / 2; j++)
            a[i] += a[j];
        a[i]++;
    }
    cout << a[n];

    return 0;
}

解法二:递归+记忆化

将递归的结果先存起来,下次遇到就不用再递归下去

复杂度: \(O(n \log n)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;

int a[1010];

int func(int n)
{
    if (n == 1)
        return 1;
    if (a[n])
        return a[n];
    int sum = 1;
    for (int i = 1; i <= n / 2; ++i)
        sum += func(i);
    return a[n] = sum;
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n;
    cin >> n;
    cout << func(n);

    return 0;
}

解法三:打表

以下是打表的代码

做表大概十分钟,打完表复杂度为 \(O(1)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;

int func(int n)
{
    if (n == 1)
        return 1;
    int sum = 1;
    for (int i = 1; i <= n / 2; i++)
        sum += func(i);
    return sum;
}

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    cout << "int a[1010]={0";
    for (int i = 1; i <= 1000; i++)
        cout << "," << func(i);
    cout << "};";

    return 0;
}

M 差分是你的谎言O.o

考点:

  • 基础语法

题解:

签到题

按照题意,用 for 构造出 \(b\) 数组即可

复杂度: \(O(n)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;

int main()
{
    cin.tie(0)->ios::sync_with_stdio(false);

    int n;
    cin >> n;
    vector<int> a(n), b(n - 1);
    for (int i = 0; i < n; i++)
        cin >> a[i];

    for (int i = 0; i < n - 1; i++)
        b[i] = abs(a[i + 1] - a[i]);

    for (int i = 0; i < n - 1; i++)
    {
        if (i)
            cout << ' ';
        cout << b[i];
    }

    return 0;
}

2023 SZTU-ACM迎新赛
http://xiaowhang.github.io/archives/4125702198/
作者
Xiaowhang
发布于
2023年12月1日
许可协议