返回顶部

SDUWH-CCCC校内选拔赛题解

L1-1 宇宙无敌大招呼

签到

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    string s;
    cin >> s;
    s = "Hello " + s;
    cout << s << "\n";
    return 0;
}

L1-2 寻找250

签到

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int x, idx = 0;
    while(cin >> x) {
        idx ++;
        if(x == 250) {
            cout << idx << "\n";
            break;
        }
    }
    return 0;
}

L1-3 装睡

签到

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);   
    int n;
    cin >> n;
    while(n -- ) {
        string s;
        int x, y;
        cin >> s >> x >> y;
        if(x < 15 or x > 20 or y < 50 or y > 70) 
            cout << s << "\n"; 
    }
    return 0;
}

L1-4 生命游戏

按要求模拟

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;
const int dx[] = {0, 0, 1, -1, 1, 1, -1, -1};
const int dy[] = {-1, 1, 0, 0, -1, 1, -1, 1};

char g[55][55], ng[55][55];

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);   
    int T;
    cin >> T;
    while(T -- ) {
        int n, m;
        cin >> n >> m;
        for(int i = 1; i <= n; ++ i) cin >> (g[i] + 1);
        for(int i = 1; i <= n; ++ i) 
            for(int j = 1; j <= m; ++ j) {
                int cnt = 0;
                for(int k = 0; k < 8; ++ k) {
                    int xx = i + dx[k], yy = j + dy[k];
                    if(xx >= 1 and yy >= 1 and xx <= n and yy <= m and g[xx][yy] == '1')
                        cnt ++;
                }
                if(g[i][j] == '1') {
                    if(cnt <= 1 or cnt >= 4) ng[i][j] = '0';
                    else ng[i][j] = '1';
                }
                else {
                    if(cnt == 3) ng[i][j] = '1';
                    else ng[i][j] = '0';
                }
            }
        for(int i = 1; i <= n; ++ i)  {  
            for(int j = 1; j <= m; ++ j)
                cout << ng[i][j];
            cout << "\n";
        }
    }
    return 0;
}

L1-5 个位数统计

按要求模拟

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);   
    string s;
    cin >> s;
    map<int, int> mp;
    for(int i = 0; i < s.size(); ++ i) 
        mp[s[i] - '0'] ++;
    for(auto [x, y] : mp)
        cout << x << ":" << y << "\n";
    return 0;
}

L1-6 整除分块

考虑\(\left\lfloor\dfrac{n}{i}\right\rfloor\) \((i = 1, 2, ..., n)\) , 该式最多只有 $2 \sqrt n $ 个取值

  • \(i \le \sqrt n\), 最多只有 \(\sqrt n\) 个不同的取值
  • \(i > \sqrt n\), 式子取值从 \(\sqrt n\)\(1\), 最多只有 \(\sqrt n\) 个不同值
    故该式最多只有 \(2\sqrt n\) 个取值

根据 \(x\) 求最大 \(t\), 使得 \(\left\lfloor\dfrac{n}{x}\right\rfloor = \left\lfloor\dfrac{n}{t}\right\rfloor\)\(\left\lfloor\dfrac{n}{x}\right\rfloor > \left\lfloor\dfrac{n}{t + 1}\right\rfloor\), \(t = \left\lfloor\dfrac{n}{\left\lfloor\dfrac{n}{x}\right\rfloor}\right\rfloor\)

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int T;
    cin >> T;
    while(T -- ) {
        int n, x;
        cin >> n >> x;
        vector<int> v;   
        for(int l = 1, r = 0; l <= n; l = r + 1) {
            r = min(n, n / (n / l));
            v.push_back(n / l);
        }
        sort(v.begin(), v.end());
        cout << lower_bound(v.begin(), v.end(), x) - v.begin() + 1 << "\n";
    }   
    return 0;
}

L1-7 点赞

按要求模拟

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int n;
    cin >> n;
    map<int, int> mp;
    for(int i = 1, k; i <= n; ++ i) {
        cin >> k;
        for(int j = 0, x; j < k; ++ j) {
            cin >> x;
            mp[x] ++;
        }
    }
    int id = 0;
    for(auto [x, y] : mp) {
        if(y >= mp[id]) id = x;
    }
    cout << id << " " << mp[id] << "\n";
    return 0;
}

L1-8 古风排版

先用空格将字符串长度补全至 \(n\) 的整数倍,然后按要求模拟即可

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int n;
    cin >> n;
    string s;
    getline(cin, s);
    getline(cin, s);
    int m = (s.size() + n - 1) / n * n;
    while(s.size() < m) s += " ";
    for(int i = n - 1; i >= 0; -- i) {
        for(int j = s.size() - 1 - i; j >= 0; j -= n) 
            cout << s[j];
        cout << "\n";
    }
    return 0;
}

L2-1 奥运排行榜

按照四种关键字排序模拟
坑点:若四个国家金牌数为 \(4\) \(3\) \(3\) \(2\),排名应该是 \(1\) \(2\) \(2\) \(4\),而不是 \(1\) \(2\) \(2\) \(3\)

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;

struct Node {
    int a, b, c, id;
};

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<Node> v(n);
    for(int i = 0; i < n; ++ i) {
        cin >> v[i].a >> v[i].b >> v[i].c;
        v[i].id = i;
    } 
    vector<vector<int>> rk(n, vector<int>(4));
    sort(v.begin(), v.end(), [&](Node x, Node y){
        return x.a > y.a;
    });
    int idx = 1;
    for(int i = 0; i < n; ++ i) {
        if(i and v[i].a != v[i - 1].a) idx = i + 1;
        rk[v[i].id][0] = idx;
    }
    sort(v.begin(), v.end(), [&](Node x, Node y){
        return x.b > y.b;
    });
    idx = 1;
    for(int i = 0; i < n; ++ i) {
        if(i and v[i].b != v[i - 1].b) idx = i + 1;
        rk[v[i].id][1] = idx;
    }
    sort(v.begin(), v.end(), [&](Node x, Node y){
        return x.a * y.c > y.a * x.c;
    });
    idx = 1;
    for(int i = 0; i < n; ++ i) {
        if(i and v[i].a * v[i - 1].c != v[i - 1].a * v[i].c) idx = i + 1;
        rk[v[i].id][2] = idx;
    }
    sort(v.begin(), v.end(), [&](Node x, Node y){
        return x.b * y.c > y.b * x.c;
    });
    idx = 1;
    for(int i = 0; i < n; ++ i) {
        if(i and v[i].b * v[i - 1].c != v[i - 1].b * v[i].c) idx = i + 1;
        rk[v[i].id][3] = idx;
    }
    for(int i = 1; i <= m; ++ i) {
        int x, t = 0;
        cin >> x;
        for(int j = 0; j < 4; ++ j) {
            if(rk[x][j] < rk[x][t])
                t = j;
        }
        cout << rk[x][t] << ':' << t + 1 << " \n"[i == m];
    }
    return 0;
}

L2-2 文件传输

并查集模板题

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    int n;
    cin >> n;
    vector<int> f(n);
    for(int i = 0; i < n; ++ i) f[i] = i;
    function<int(int)> getf = [&](int x) {
        return x == f[x] ? f[x] : f[x] = getf(f[x]);
    };
    char op;
    while(cin >> op) {
        if(op == 'S') {
            int cnt = 0;
            for(int i = 0; i < n; ++ i) cnt += f[i] == i;
            if(cnt == 1) cout << "The network is connected.\n";
            else cout << "There are "<< cnt << " components.\n";
            break;
        }
        int x, y;
        cin >> x >> y;
        x --, y --;
        int fx = getf(x), fy = getf(y);
        if(op == 'I') {
            if(fx != fy) 
                f[fx] = fy;
        }
        else {
            if(fx == fy) cout << "yes\n";
            else cout << "no\n";
        }
    }
    return 0;
}

L2-3 功夫传人

遍历树,统计答案

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int n;
double ans, z, r;
vector<int> g[100010], b;

void dfs(int u, double val) {
    if(b[u]) ans += val * b[u];
        else {
            for(auto v : g[u]) dfs(v, val * (100.0 - r) / 100.0); 
        }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n >> z >> r;
    b.resize(n);
    for(int i = 0, k; i < n; ++ i) {
        cin >> k;
        if(not k) cin >> b[i];
        for(int j = 0, x; j < k; ++ j) {
            cin >> x;
            g[i].push_back(x);
        }
    }
    dfs(0, z);
    cout << (LL)ans << "\n";
    return 0; 
}

L2-4 玩转二叉树

根据中序遍历和前序遍历建树,按要求层序遍历即可

Sample Code (C++)
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;

int n, in[N], pre[N], lc[N], rc[N];

int solve(int lin, int rin, int lpre, int rpre) {
    if(lin > rin) return 0;
    int rt = pre[lpre];
    int p = lin;
    while(in[p] != rt) p ++;
    lc[rt] = solve(lin, p - 1, lpre + 1, lpre + p - lin);
    rc[rt] = solve(p + 1, rin, lpre + p - lin + 1, rpre);
    return rt;
}

vector<int> p[32];

void dfs(int u, int dep) {
    p[dep].push_back(u);
    if(lc[u]) dfs(lc[u], dep + 1);
    if(rc[u]) dfs(rc[u], dep + 1);
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n;
    for(int i = 0; i < n; ++ i) cin >> in[i];
    for(int i = 0; i < n; ++ i) cin >> pre[i];
    solve(0, n - 1, 0, n - 1);
    dfs(pre[0], 0);
    int cnt = 0;
    for(int i = 0; i < 30; ++ i) {
        reverse(p[i].begin(), p[i].end());
        for(int j = 0; j < p[i].size(); ++ j) {
            cnt ++;
            cout << p[i][j] << " \n"[cnt == n];
        }
    }
    return 0;
}

L3-1 森森旅游

先预处理从 \(1\)\(n\) 出发的花费,即在原图 \(G\) 上从起点 \(1\) 以现金为边权跑一遍 \(dijkstra\), 将原图的边反向建图得到 \(rG\), 在 \(rG\) 上从终点 \(n\) 以旅游金为边权跑一遍 \(dijkstra\), 然后用一个 \(multiset\) 维护在 \(n\) 个城市兑换旅游金的现金花费,可以支持动态修改,且可以取最小值

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
using PII = pair<int, int>;
using LL = long long;
using PLI = pair<LL, int>;
const LL INF = 1e18;

vector<PII> G[N], rG[N];
int n, m, q, a[N];
LL dis[N], rdis[N], res[N];
bool st[N];

void dijkstra(int S, LL dis[], int op) {
    for(int i = 1; i <= n; ++ i) {
        dis[i] = INF;
        st[i] = false;
    }
    priority_queue<PLI, vector<PLI>, greater<PLI>> Q;
    Q.push({0, S});
    dis[S] = 0;
    while(!Q.empty()) {
        int u = Q.top().second;
        Q.pop();
        if(st[u]) continue;
        st[u] = true;
        if(op) {
            for(auto &[v, w] : G[u]) 
                if(dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    Q.push({dis[v], v});
                }
        }
        else {
            for(auto &[v, w] : rG[u]) {
                if(dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    Q.push({dis[v], v});
                }
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n >> m >> q;
    for(int i = 0; i < m; ++ i) {
        int u, v, c, d;
        cin >> u >> v >> c >> d;
        G[u].push_back({v, c});
        rG[v].push_back({u, d});
    }
    for(int i = 1; i <= n; ++ i) cin >> a[i];
    dijkstra(1, dis, 1);
    dijkstra(n, rdis, 0);
    multiset<LL> S;
    for(int i = 1; i <= n; ++ i) {
        if(dis[i] == INF or rdis[i] == INF) continue;
        res[i] = dis[i] + (rdis[i] + a[i] - 1) / a[i];
        S.insert(res[i]);
    }
    while(q -- ) {
        int x, v;
        cin >> x >> v;
        if(dis[x] != INF and rdis[x] != INF) { 
            auto it = S.find(res[x]);
            S.erase(it);
            res[x] = dis[x] + (rdis[x] + v - 1) / v;
            S.insert(res[x]);
        }
        cout << *S.begin() << "\n";
    }
    return 0; 
}

L3-2 至多删三个字符

\(dp[i][j]\) 为前 \(i\) 个字符删除了 \(j\) 个,得到不同字符串的个数
首先考虑一个简单问题,不考虑删除字符后得到字符串的重复
那么有状态转移方程: \(dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]\)
现在考虑如何去重,例如串 \(******a**a***\),设第一个 'a' 的位置为 \(k\), 第二个 'a' 的位置为 \(i\)
当我们删除从 \(k\) 开始的 \(a**\) 和 删除到 \(i\) 结束的 \(**a\) 时,得到的字符串相同,需要去重的情况是第一个 'a' 字符前面(即 \(k - 1\)) 去除 \(i - 3\) 个字符的情况,即为 \(dp[k - 1][j - (i - k)]\),故当出现相同字符时按照上述去重方式去重即可

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1000010;

LL dp[N][4];
char s[N];

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> (s + 1);
    int n = strlen(s + 1);
    dp[0][0] = 1;
    for(int i = 1; i <= n; ++ i)
        for(int j = 0; j <= 3; ++ j) {
            if(i < j) continue;
            dp[i][j] = dp[i - 1][j];
            if(j >= 1) dp[i][j] += dp[i - 1][j - 1];
            for(int k = i - 1; k >= 1 && i - k <= j; -- k) 
                if(s[i] == s[k]) {
                    dp[i][j] -= dp[k - 1][j - (i - k)]; 
                    break;
                }
        }
    LL res = 0;
    for(int i = 0; i <= 3; ++ i) res += dp[n][i];
    cout << res << "\n";
    return 0;
        
}

L3-3 胜利者集合

首先枚举每一个玩家 \(x\) 是否可以成为胜利者,当对一个玩家进行判断时,他的胜场应尽可能多,那么他还未进行的比赛将全部获胜。
此时假设他的胜场为 \(win\), 于是问题转化为当其他人的胜场最大为 \(win - 1\) 的情况下,是否可以构造出一种合法方案?
对于上述问题,可以转化为一个最大流的问题,考虑如何建图:

  • 建立两组点,第一组点是比赛(除去与 \(x\) 进行的比赛),第二组点是玩家(除去 \(x\)),流量即为胜利
  • 从源点 \(S\) 向所有比赛连接一条容量为 \(1\) 的边
  • 若当前比赛未比,则从当前比赛到两个玩家各连一条容量为 \(1\) 的边
  • 若当前比赛已比,则从当前比赛到胜者连一条容量为 \(1\) 的边
  • 若某玩家胜过 \(x\),则从当前玩家向汇点 \(T\) 连一条容量为 \(win - 2\) 的边
  • 若某玩家输给 \(x\),则从当前玩家向汇点 \(T\) 连一条容量为 \(win - 1\) 的边

判断最大流是否满流,若满流,则存在一种合法方案,即该玩家 \(x\) 可以成为胜利者

Sample Code (C++)
#include <bits/stdc++.h>
using namespace std;
const int N = 1500, M = 10010, INF = 1e9;

int n, m, S, T;
struct Edge {
    int to, nxt, flow;
}line[M];
int fist[N], idx, d[N], cur[N];

void add(int x, int y, int z) {
    line[idx] = {y, fist[x], z}; fist[x] = idx ++;
    line[idx] = {x, fist[y], 0}; fist[y] = idx ++;
}

bool bfs() {
    queue<int> q;
    memset(d, -1, sizeof d);    
    q.push(S), d[S] = 0, cur[S] = fist[S];
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int i = fist[u]; i != -1; i = line[i].nxt) {
            int v = line[i].to;
            if(d[v] == -1 && line[i].flow) {
                d[v] = d[u] + 1;
                cur[v] = fist[v];
                if(v == T) return 1;
                q.push(v); 
            }
        }
    }
    return 0;
}

int find(int u, int limit) {
    if(u == T) return limit;
    int flow = 0;
    for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt) {
        cur[u] = i;
        int v = line[i].to;
        if(d[v] == d[u] + 1 && line[i].flow) {
            int t = find(v, min(line[i].flow, limit - flow));
            if(!t) d[v] = -1;
            line[i].flow -= t;
            line[i ^ 1].flow += t;
            flow += t;
        }
    }
    return flow;
}

int dinic() {
    int res = 0, flow;
    while(bfs()) while(flow = find(S, INF)) res += flow;
    return res;
}

int main() {
    ios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n >> m;
    vector<vector<int>> g(n, vector<int>(n, 0));
    for(int i = 0; i < m; ++ i) {
        int u, v;
        cin >> u >> v;
        u --, v --;
        g[u][v] = 1, g[v][u] = -1;
    }
    S = n + (n - 2) * (n - 1) / 2;
    T = S + 1;
    int flag = 0;
    for(int i = 0; i < n; ++ i) {
        int win = n - 1;
        for(int j = 0; j < n; ++ j) 
            if(g[i][j] == -1) win --;
        idx = 0;
        memset(fist, -1, sizeof fist);
        int cur = n;
        for(int j = 0; j < n; ++ j) {
            if(j == i) continue;
            for(int k = j + 1; k < n; ++ k) {
                if(k == i) continue;
                add(S, cur, 1);
                if(g[j][k] == 0) {
                    add(cur, j, 1);
                    add(cur, k, 1);
                }
                if(g[j][k] == 1)
                    add(cur, j, 1);
                if(g[j][k] == -1)
                    add(cur, k, 1);
                cur ++;
            }
        }
        bool ok = true;
        for(int j = 0; j < n; ++ j) {
            if(win - 1 - (g[j][i] > 0) < 0) {
                ok = false;
                break;
            }
            if(j != i) 
                add(j, T, win - 1 - (g[j][i] > 0));
        }
        if(!ok) continue;
        if(dinic() == (n - 1) * (n - 2) / 2) {
            if(flag) cout << ' ';
            cout << i + 1;
            flag = 1;
        }
    }
    cout << '\n';
    return 0;
}
posted @ 2022-03-18 15:47  __October  阅读(189)  评论(0)    收藏  举报