WUSTACM2021暑假集训日记:7.7

本文分享了作者在编程竞赛中的解题经验,包括CF Round #730 (Div.2)的比赛经历。文章通过四个具体的题目实例,探讨了如何避免使用map可能导致的错误,以及在没有必须使用map的情况下选择其他数据结构。作者提到了贪心策略、前缀和、最短路径等算法,并提供了相应的代码实现。此外,还分享了在找不到解题思路时如何通过观察和尝试找到规律的方法。
摘要由CSDN通过智能技术生成

7.7

白天补题,晚上打了场CF;

CodeForces个人赛补题 : Training 1

B. Linear Algebra Test

传送门

​ map初始化了一下,结果wa了,暂时不知道为啥;

#include <bits/stdc++.h>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;

int n;
ll ans;
map<int, int> mpa, mpb;

void solve() {
    for (int i = 0; i < n; i++) {
        int a, b;
        scanf("%d%d", &a, &b);

        mpa[a]++;
        mpb[b]++;
    }

    for (map<int, int>::iterator it = mpa.begin(); it != mpa.end(); it++) {
        ans += mpa[it->first] * mpb[it->first];
    }

    printf("%lld\n", ans);
}

void init() {
    ans = 0;
    mpa.clear();
    mpb.clear();
}

signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    scanf("%d", &t);
    while (t--) {
        init();
        scanf("%d", &n);
        solve();
    }

    return 0;
}

​ 于是换了一个写法就a了,只是把乘改成了加;

#include <bits/stdc++.h>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;

int n;
ll ans;
map<int, int> mpa;
vector<int> B;

void solve() {
    for (int i = 0; i < n; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        B.push_back(b);
        mpa[a]++;
    }

    for(auto i : B) {
        ans += mpa[i];
    }

    printf("%lld\n", ans);
}

void init() {
    ans = 0;
    mpa.clear();
    B.clear();
}

signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    scanf("%d", &t);
    while (t--) {
        init();
        scanf("%d", &n);
        solve();
    }

    return 0;
}

D. Dice Game

传送门

​ bfs,用一个Node来存三个值:now(当前朝上的数字),v(累积到这一步的值),stp(到这一步所用的步骤),然后枚举与当前向上数字相邻的数字(也即除了相加等于7以外的所有情况)即可,注意,如果累积的值已经出现过就没有必要入队了,因为这个bfs中先出现过的值肯定要比后出现的用的步骤更少,这里用一个vis数组来记录;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;

int n;
bool vis[maxn];
struct Node {
    int now, v, stp;
};

void bfs() {
    queue<Node> q;
    q.push(Node{ 1, 0, 0 });
    while (!q.empty()) {
        Node tmp = q.front();
        q.pop();

        for (int i = 1; i <= 6; i++) {
            if (i == tmp.now || i + tmp.now == 7 || vis[tmp.v + i])
                continue;

            int v = tmp.v + i;
            int now = i;
            int stp = tmp.stp + 1;
            vis[v] = true;
            if (v == n) {
                cout << stp << '\n';
                return;
            }
            q.push(Node{ now, v, stp });
        }
    }

    cout << -1 << '\n';
}

void solve() {
    if (n == 1) {
        cout << -1 << '\n';
        return;
    }
    bfs();
}

void init() {
    mms(vis, false);
}

signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        init();
        solve();
    }

    return 0;
}

F. Building Numbers

传送门

​ 贪心 + 前缀和,贪心策略如果a[i]是偶数就除二,如果是奇数就减一,这样一直到1就是build这个数字的最小步骤。然后把数组里所有数的build最小步骤存于一个前缀和查询就行;

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;

int n, q;
ll num[maxn], pre[maxn];

void solve() {
    for (int i = 1; i <= n; ++i) {
        ll tmp;
        int cnt = 0;
        cin >> tmp;
        while(tmp != 1) {
            if(tmp & 1) {
                --tmp;
                ++cnt;
            } else {
                tmp /= 2;
                ++cnt;
            }
        }
        num[i] = cnt;
        pre[i] = cnt + pre[i - 1];
    }

    while(q--) {
        int l, r;
        cin >> l >> r;
        cout << pre[r] - pre[l - 1] << '\n';
    }
}

void init() {
    pre[0] = 0;
    num[0] = 0;
}

signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        init();
        cin >> n >> q;
        solve();
    }

    return 0;
}

I. Move Between Numbers

传送门

​ 最短路,跑了一个堆优化的dijkstra,存图的时候需要处理一下,把每个字符串中0~9出现的次数统计一下,然后根据题目意思建图,不过题目中已经给出了公式,直接套就行;

#include <bits/stdc++.h>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 250 + 5;

int n, s, e;
int dis[maxn];
int arr[maxn][maxn];
struct Edge {
    int v, w;
};
vector<Edge> G[maxn];
struct Node {
    int u, dis;
    friend bool operator<(const Node& a, const Node& b) {
        return a.dis > b.dis;
    }
};

inline void AddEdge(int u, int v) {
    G[u].push_back(Edge{ v, 1 });
}

void Dijkstra() {
    bool vis[maxn];
    for (int i = 1; i <= n; ++i) {
        vis[i] = false;
        dis[i] = inf;
    }
    dis[s] = 0;
    priority_queue<Node> q;
    q.push(Node{ s, 0 });

    while (!q.empty()) {
        Node tmp = q.top();
        q.pop();
        int u = tmp.u, d = tmp.dis;

        if (vis[u]) 
            continue;
        else 
            vis[u] = true;

        for (int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i].v, w = G[u][i].w;
            if (d + w < dis[v]) {
                dis[v] = d + w;
                q.push(Node{ v, dis[v] });
            }
        }
    }
}

bool jdg(int a, int b) {
    int cnt = 0;
    for(int i = 0; i <= 9; i++) {
        cnt += min(arr[a][i], arr[b][i]);
    }
    return cnt == 17;
}

void solve() {
    for (int i = 1; i <= n; ++i) {
        string str;
        cin >> str;
        for(int j = 0; j < 20; ++j) {
            ++arr[i][str[j] - '0'];
        }
    }

    for (int i = 1; i <= n; ++i) {
        for (int j = i + 1; j <= n; ++j) {
            if (jdg(i, j)) {
                AddEdge(i, j);
                AddEdge(j, i);
            }
        }
    }

    Dijkstra();

    if(dis[e] == inf)
        cout << -1 << '\n';
    else
        cout << dis[e] << '\n';
}

void init() {
    mms(arr, 0);
    for (int i = 1; i <= n; ++i) {
        G[i].clear();
    }
}

signed main() {
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        init();
        cin >> n >> s >> e;
        solve();
    }

    return 0;
}

感觉每次用map总会出一些奇奇怪怪的错误,现阶段在没有必须使用map的时候还是先不考虑map吧;

CodeForces Round #730 (Div.2)

传送门

​ 蒟蒻的我当然还是只a了两题,还是半猜半蒙的;

A. Exciting Bets

传送门

​ 半天没思路,看了半天找了个规律:

​ 输出的前一个数是c = abs(a - b),即a和b的差值;

​ 后面一个数min(m, n),m为a % c 或者 b % c,即a或b对c取模;n为c - m,即c减去这个模;

​ 没有任何的推理根据,就是纯玄学的找规律;

#include <bits/stdc++.h>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 2e6;

ll a, b;

void solve() {
    ll c = abs(a - b);
    ll m = a % c, n = c - m;
    cout << c << ' ' << min(m, n) << '\n';
}

signed main() {
    std::ios::sync_with_stdio(false);

    int t;
    cin >> t;
    while(t--) {
        cin >> a >> b;
        if(a == b) {
            cout << "0 0" << '\n';
            continue;
        }
        solve();
    }

    return 0;
}

B. Customising the Track

传送门

​ 依然是毫无根据的玄学找规律贪心,求出数列的平均值,用平均值以上的个数 * 平均值以下的个数就是正解,具体是为啥我也不知道;

#include <bits/stdc++.h>
#define ll long long
#define mms(a, b) memset(a, b, sizeof(a))
using namespace std;

const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 2e6;

int n;

void solve() {
    ll sum = 0;
    for(int i = 0; i < n; ++i) {
        int num;
        cin >> num;
        sum += num;
    }
    ll ave = sum / n;
    ll a = sum - n * ave;
    ll b = n - a;
    cout << a * b << '\n';
}

signed main() {
    std::ios::sync_with_stdio(false);

    int t;
    cin >> t;
    while(t--) {
        cin >> n;
        solve();
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Luther_w

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

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

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

打赏作者

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

抵扣说明:

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

余额充值