2018 USP Try-outs 补题

题目链接

https://codeforces.com/gym/101879

参考题解

B - Aesthetics in poetry

简要题意:

给一个长度为 n n n 的数组 a i a_i ai,再给一个数 k k k,求最小的 j j j 满足: j j j 大于 1 1 1 且整除 n n n,并且 a i a_i ai 除以 k k k 的余数恰有 k k k 种,每种有 n k \frac{n}{k} kn 个。无解输出 − 1 -1 1

解题思路:

范围不大,暴力判断。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
int a[maxn], cnt[maxn];
int n;
 
int check(int k){
 
    if(n % k) return 0;
    for(int i = 1; i <= n; ++i){
 
        ++cnt[a[i] % k];
    }
    int ret = 1;
    for(int i = 0; i < k; ++i){
 
        if(cnt[i] != n / k) { ret = 0; break; }
    }
    for(int i = 0; i < k; ++i) cnt[i] = 0;
    return ret;
}
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    int ret = -1;
    for(int i = 2; i <= n; ++i){
 
        if(check(i)) { ret = i; break; }
    }
    cout << ret << endl;
    return 0;
}

C - Promenade by the lake

简要题意:

给一个 n n n 个顶点、 m m m 条边的无向图,再给 k k k 条侯选边,问能否添加若干条侯选边使得原图有欧拉回路。

解题思路:

无向图有欧拉回路当且仅当所有点度数为偶数,侯选边组成的图中,若有环,则去掉环对答案无影响,故侯选边构成一棵树,接着就变成一个树形 d p dp dp 问题:每条边可选或不选、要让所有点度数为偶数,可以自底向上逐一确定答案。(这里犯傻写了树形 d p dp dp,输出答案部分需要好好考虑一下)

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 6e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
vector<pii> G[maxn], P[maxn][2], ans;
vector<int> rt;
pii ei[maxn];
int deg[maxn], dp[maxn][2], pre[maxn], vis[maxn];
int n, m, k;
 
int fin(int x){
 
    return x == pre[x] ? x : pre[x] = fin(pre[x]);
}
 
int dfs(int u, int f){
 
    dp[u][deg[u]] = 1, vis[u] = 1;
    int pu = u, p = u;
    for(auto &e : G[u]){
 
        int v = e.first;
        if(v == f) continue;
        int pv = dfs(v, u); p = e.second;
        // dp[p][0] |= dp[pu][0] & dp[pv][0];
        // dp[p][0] |= dp[pu][1] & dp[pv][1]; // xuan
        // dp[p][1] |= dp[pu][0] & dp[pv][1]; // xuan
        // dp[p][1] |= dp[pu][1] & dp[pv][0];
        for(int i = 0; i < 2; ++i){
 
            for(int j = 0; j < 2; ++j){
 
                if(dp[pu][i] && dp[pv][j]){
 
                    dp[p][i ^ j] = 1;
                    P[p][i ^ j].pb(pii{pu, i});
                    P[p][i ^ j].pb(pii{pv, j});
                }
            }
        }
        pu = p;
    }
    return p;
}
 
void dfs2(int x, int y){
 
    if(x <= n) return;
    auto e1 = P[x][y][0], e2 = P[x][y][1];
    if(y != e1.second) ans.pb(ei[x - n]);
    dfs2(e1.first, e1.second);
    dfs2(e2.first, e2.second);
}
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> k;
    for(int i = 1; i <= m; ++i){
 
        int u, v; cin >> u >> v;
        deg[u] ^= 1, deg[v] ^= 1;
    }
    for(int i = 1; i <= n; ++i){
 
        pre[i] = i;
    }
    for(int i = 1; i <= k; ++i){
 
        int u, v; cin >> u >> v;
        if(fin(u) == fin(v)) continue;
        pre[fin(u)] = fin(v);
        G[u].pb(pii{v, n + i}), G[v].pb(pii{u, n + i});
        ei[i] = pii{u, v};
        // cout << u << " -> " << v << endl;
    }
    // for(int i = 1; i <= n; ++i){
 
    //     cout << i << " ?? " << deg[i] << endl;
    // }
    int ret = 1;
    for(int i = 1; i <= n; ++i){
 
        if(vis[i]) continue;
        int u = dfs(i, 0);
        ret &= dp[u][0];
        rt.pb(u);
    }
    // for(int i = 1; i <= n + k; ++i){
 
    //     cout << i << " ?? " << dp[i][0] << " " << dp[i][1] << endl;
    // }
    if(!ret){
 
        cout << "NO" << endl;
        return 0;
    }
    cout << "YES" << "\n";
    for(auto &v : rt){
 
        dfs2(v, 0);
    }
    cout << sz(ans) << "\n";
    for(auto &e : ans){
 
        cout << e.first << " " << e.second << "\n";
    }
    return 0;
}

D - Maximizing Advertising

简要题意:

二维平面给 n n n 个点,点为黑色或白色,现要用两个不相交的矩形分别圈住某些点,要求让某个矩形的白点与另一个矩形的黑点个数和最大,求最大值。

解题思路:

可通过反证得知两个矩形圈住所有点,并且边界在点坐标处,并且要么一左一右、要么一上一下,分别枚举 x , y x,y x,y 分割处即可。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
struct Node{
    int x, y, c;
} a[maxn];
int n;
 
int cmpX(const Node &a, const Node &b){
 
    return a.x < b.x;
}
 
int cmpY(const Node &a, const Node &b){
 
    return a.y < b.y;
}
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    int tot[2] = {};
    for(int i = 1; i <= n; ++i){
 
        int x, y; char ch[2]; cin >> x >> y >> ch;
        a[i] = Node{x, y, ch[0] == 'b'};
        tot[ch[0] == 'b']++;
    }
    int ret = 0;
    sort(a + 1, a + 1 + n, cmpX);
    int cnt[2] = {};
    for(int l = 1, r; l <= n; l = r){
 
        r = l;
        while(r <= n && a[r].x == a[l].x){
 
            ++cnt[a[r].c];
            ++r;
        }
        ret = max(ret, cnt[0] + (tot[1] - cnt[1]));
        ret = max(ret, cnt[1] + (tot[0] - cnt[0]));
    }
    sort(a + 1, a + 1 + n, cmpY);
    cnt[0] = cnt[1] = 0;
    for(int l = 1, r; l <= n; l = r){
 
        r = l;
        while(r <= n && a[r].y == a[l].y){
 
            ++cnt[a[r].c];
            ++r;
        }
        ret = max(ret, cnt[0] + (tot[1] - cnt[1]));
        ret = max(ret, cnt[1] + (tot[0] - cnt[0]));
    }
    cout << ret << endl;
    return 0;
}

E - Group work

简要题意:

签到题。

解题思路:

签到题。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 6e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    int n; cin >> n;
    cout << (1 << n) - n - 1 << endl;
    return 0;
}

F - Optimizing Transportation in Portugal

简要题意:

给定一个 n n n 个顶点、 m m m 条带权边的无向图,再给起点 s s s 和终点 t t t,问删去一条边,最多能让 s s s t t t 的最短路变成多长。

解题思路:

s s s t t t 随便扣一条最短路 P P P,只有删除 P P P 上的边才对答案有影响,枚举删边跑最短路显然不可取,故反向考虑其他边对 P P P 删边后最短路的贡献。枚举边 ( u , v , w ) (u, v, w) (u,v,w),考虑 s → u → v → t s \rightarrow u \rightarrow v \rightarrow t suvt,要在这条路径最短的情况下,使得经过 P P P 的前缀、后缀部分尽可能短,即需要找到 u u u P P P 上距离 s s s最近的点 x x x v v v P P P 上距离 t t t 最近的点 y y y,如果删去 x x x y y y 的边,就仍存在一条 s → u → v → t s \rightarrow u \rightarrow v \rightarrow t suvt 的最短路径。 x x x y y y 可以通过一次拓扑转移得到,贡献表现为区间内有贡献,扫描线处理即可。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 5e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
const ll oo = 1ll << 60;
typedef pair<ll, int> pli;
struct Edge{
    int u, v, w, p;
};
vector<ll> val[maxn];
vector<Edge> G[maxn], G1[maxn], G2[maxn];
ll dis1[maxn], dis2[maxn];
int in[maxn], dep1[maxn], dep2[maxn], low1[maxn], low2[maxn], hasE[maxn], preV[maxn], preE[maxn];
int n, m, S, T, tot;
 
void dij(int s, ll dis[]){
 
    priority_queue<pli, vector<pli>, greater<pli> > q;
    for(int i = 1; i <= n; ++i) dis[i] = oo;
    dis[s] = preV[s] = preE[s] = 0, q.push({dis[s], s});
    while(!q.empty()){
 
        int u = q.top().second; ll d = q.top().first; q.pop();
        for(auto &e : G[u]){
 
            if(dis[e.v] > dis[u] + e.w){
 
                dis[e.v] = dis[u] + e.w;
                preV[e.v] = u, preE[e.v] = e.p;
                q.push({dis[e.v], e.v});
            }
        }
    }
}
 
void build(vector<Edge> g[], ll dis[], int dep[], int low[]){
 
    for(int i = 1; i <= n; ++i){
 
        in[i] = 0;
        low[i] = dep[i] ? dep[i] : n + 1;
    }
    for(int i = 1; i <= n; ++i){
 
        for(auto &e : G[i]){
 
            if(dis[e.v] == dis[i] + e.w) g[i].pb(Edge{i, e.v, e.w, e.p}), ++in[e.v];
        }
    }
    queue<int> q;
    for(int i = 1; i <= n; ++i){
 
        if(in[i] == 0) q.push(i);
    }
    while(!q.empty()){
 
        int u = q.front(); q.pop();
        for(auto &e : g[u]){
 
            if(!dep[e.v]) low[e.v] = min(low[e.v], low[u]);
            if(--in[e.v] == 0) q.push(e.v);
        }
    }
}
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> S >> T;
    for(int i = 1; i <= m; ++i){
 
        int u, v, w; cin >> u >> v >> w;
        G[u].pb(Edge{u, v, w, i});
        G[v].pb(Edge{v, u, w, i});
    }
    dij(S, dis1);
    dij(T, dis2);
    // cout << dis1[T] << endl;
    for(int u = S; u; u = preV[u]){
 
        dep1[u] = ++tot, hasE[preE[u]] = tot;
        // cout << u << " ";
    }
    // cout << endl;
    for(int u = S; u; u = preV[u]){
 
        dep2[u] = tot - dep1[u] + 1;
    }
    build(G1, dis1, dep1, low1);
    build(G2, dis2, dep2, low2);
    for(int i = 1; i <= n; ++i){
 
        low2[i] = tot - low2[i] + 1;
        // cout << i << " ?? " << low1[i] << " " << low2[i] << endl;
    }
    for(int i = 1; i <= n; ++i){
 
        for(auto &e : G[i]){
 
            if(low1[e.u] >= low2[e.v] || hasE[e.p]) continue;
            ll d = dis1[e.u] + e.w + dis2[e.v];
            val[low1[e.u]].pb(d);
            val[low2[e.v]].pb(-d);
            // cout << low1[e.u] << " -> " << low2[e.v] << " " << d << endl;
        }
    }
    ll ret = dis1[T];
    multiset<ll> st;
    for(int i = 1; i < tot; ++i){
 
        sort(val[i].begin(), val[i].end());
        for(auto &v : val[i]){
 
            if(v < 0) st.erase(st.find(-v));
            else st.emplace(v);
        }
        ll tmp = st.empty() ? oo : *st.begin();
        // if(tmp == oo) cout << i << " ?? " << endl;
        ret = max(ret, tmp);
    }
    cout << (ret < 2e9 ? ret : -1) << endl;
    return 0;
}

G - Running a penitentiary

简要题意:

给定 n n n 个区间, q q q 次操作,每次要么修改一个区间,要么询问 [ l , r ] [l, r] [l,r] 编号的区间交长度。

解题思路:

交区间为 [ max ⁡ { l i } , min ⁡ { r i } ] [\max \{l_i\}, \min\{r_i\}] [max{li},min{ri}],分别维护区间左右端点的最值即可。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
int li[maxn], ri[maxn];
int n, q;
 
struct SegTree{
 
    int mx[maxn << 2];
    void pushUp(int rt){
 
        mx[rt] = max(mx[lson], mx[rson]);
    }
    void build(int l, int r, int rt, int a[]){
 
        if(l == r){
 
            mx[rt] = a[l];
            return;
        }
        int mid = gmid;
        build(l, mid, lson, a);
        build(mid + 1, r, rson, a);
        pushUp(rt);
    }
    void update(int l, int r, int rt, int pos, int val){
 
        if(l == r){
 
            mx[rt] = val;
            return;
        }
        int mid = gmid;
        if(pos <= mid) update(l, mid, lson, pos, val);
        else update(mid + 1, r, rson, pos, val);
        pushUp(rt);
    }
    int query(int l, int r, int rt, int L, int R){
 
        if(l >= L && r <= R) return mx[rt];
        int mid = gmid, ret = -inf;
        if(L <= mid) ret = max(ret, query(l, mid, lson, L, R));
        if(R > mid) ret = max(ret, query(mid + 1, r, rson, L, R));
        return ret;
    }
} tr_l, tr_r;
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= n; ++i){
 
        cin >> li[i] >> ri[i];
        ri[i] = -ri[i];
    }
    tr_l.build(1, n, 1, li);
    tr_r.build(1, n, 1, ri);
    while(q--){
 
        char ch[2]; int x, y, z; cin >> ch >> x >> y;
        if(ch[0] == 'C'){
 
            cin >> z;
            tr_l.update(1, n, 1, x, y);
            tr_r.update(1, n, 1, x, -z);
        }
        else{
 
            int l = tr_l.query(1, n, 1, x, y);
            int r = -tr_r.query(1, n, 1, x, y);
            int ret = l > r ? 0 : r - l + 1;
            cout << ret << "\n";
        }
    }
    return 0;
}

H - Wine Production

简要题意:

给定长度为 n n n 的数组 a i a_i ai q q q 次询问,每次询问区间 [ l , r ] [l, r] [l,r] 最大的 k k k,满足区间至少有 k k k 个不同的数,每个重复 k k k 次及以上。

解题思路:

莫队可做,答案 k k k 不超过 n \sqrt{n} n ,区间转移时维护每种频率的数的个数,统计时枚举答案即可。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 3e4 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
int a[maxn], xi[maxn], cntX;
int id[maxn], cnt[maxn], sum[maxn], ans[maxn];
int n, m;
 
struct Qr{
    int l, r, p;
    bool operator < (const Qr &o) const{
        return id[l] == id[o.l] ? (id[l] & 1 ? r > o.r : r < o.r) : id[l] < id[o.l];
    }
} qr[maxn];
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; ++i){
 
        cin >> a[i];
        xi[++cntX] = a[i];
    }
    for(int i = 1; i <= m; ++i){
 
        cin >> qr[i].l >> qr[i].r;
        qr[i].p = i;
    }
    sort(xi + 1, xi + 1 + cntX);
    cntX = unique(xi + 1, xi + 1 + cntX) - xi - 1;
    for(int i = 1; i <= n; ++i){
 
        a[i] = lower_bound(xi + 1, xi + 1 + cntX, a[i]) - xi;
    }
    int len = sqrt(n);
    for(int i = 1; i <= n; ++i){
 
        id[i] = (i - 1) / len + 1;
    }
    sort(qr + 1, qr + 1 + m);
    int l = 1, r = 0, cntH = 0;
    auto add = [&](int x){
 
        --sum[cnt[x]];
        ++cnt[x];
        ++sum[cnt[x]];
        if(cnt[x] == len + 1) ++cntH;
    };
    auto del = [&](int x){
 
        --sum[cnt[x]];
        --cnt[x];
        ++sum[cnt[x]];
        if(cnt[x] == len) --cntH;
    };
    for(int i = 1; i <= m; ++i){
 
        while(l > qr[i].l) --l, add(a[l]);
        while(r < qr[i].r) ++r, add(a[r]);
        while(l < qr[i].l) del(a[l]), ++l;
        while(r > qr[i].r) del(a[r]), --r;
        int cur = cntH;
        for(int j = len; j >= 1; --j){
 
            cur += sum[j];
            if(cur >= j) { ans[qr[i].p] = j; break; }
        }
    }
    for(int i = 1; i <= m; ++i){
 
        cout << ans[i] << "\n";
    }
    return 0;
}

I - A story about tea

简要题意:

给一个 n n n 个盘子的汉诺塔,问能否恰在 k k k 次移动后完成。

解题思路:

k < 2 n − 1 k \lt 2^n - 1 k<2n1,则无解。否则考虑 x = k − ( 2 n − 1 ) x = k - (2^n - 1) x=k(2n1),若 x x x 为偶数,让第一个盘子反复横跳即可;若 x x x 为奇数,反复横跳后剩一次,在最后一步还原时额外移动一次。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
typedef pair<char, char> pcc;
vector<pcc> ans;
int n, k;
 
void dfs(char A, char B, char C, int n){
 
    if(!n) return;
    dfs(A, C, B, n - 1);
    ans.pb(pcc{A, C});
    dfs(B, A, C, n - 1);
}
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> k;
    if(k < (1 << n) - 1){
 
        cout << "N" << endl;
        return 0;
    }
    cout << "Y" << "\n";
    int res = k - ((1 << n) - 1);
    while(res > 1){
 
        cout << "A B" << "\n";
        cout << "B A" << "\n";
        res -= 2;
    }
    dfs('A', 'B', 'C', n);
    for(int i = 0; i < sz(ans) - 1; ++i){
 
        cout << ans[i].first << " " << ans[i].second << "\n";
    }
    auto e = ans.back();
    if(res){
 
        if(e.first == 'A'){
 
            cout << "A B" << "\n";
            cout << "B C" << "\n";
        }
        else{
 
            cout << "B A" << "\n";
            cout << "A C" << "\n";
        }
    }
    else{
        
        cout << e.first << " " << e.second << "\n";
    }
    return 0;
}

J - Meme Wars

简要题意:

签到题。

解题思路:

暴力拼接。

参考代码:
#include<bits/stdc++.h>
using namespace std;
#define pb emplace_back
#define sz(a) ((int)a.size())
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
 
int main(){
 
    ios::sync_with_stdio(0); cin.tie(0);
    int n; cin >> n;
    string s = "";
    for(char i = 'a'; i <= 'z' && sz(s) < n; ++i){
 
        s = s + i + s;
    }
    cout << s[n - 1] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值