2024 暑假友谊赛 1

AtCoder abc204_d

一开始想着贪心,试了下wa掉了,然后看着过的人挺多的还是觉得是贪心🤡(试了好几遍

思路:答案最小为sum/2,那么就是找到大于等于sum/2的最小子集和,上dp,f[i][j]表示前i个数中是否存在子集和为j(看数据范围也可以看出是dp的

void solve() {
    int n;
    cin >> n;
    vector<int> ve(n + 1);
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> ve[i];
        sum += ve[i];
    }

    vector<vector<int> > f(n + 1, vector<int> (sum + 1));
    f[0][0] = 1;
    int ans = LLONG_MAX;
    for (int i = 1; i <= n; ++i) {
        for (int j = ve[i]; j <= sum; ++j) {
            f[i][j] = f[i - 1][j] | f[i - 1][j - ve[i]];
        }
    }
    for (int i = 1; i <= sum; ++i) {
        if (f[n][i]) {
            ans = min(ans, max(i, sum - i));
        }
    }
    cout << ans;

}

AtCoder arc092_a

思路:

可以直接用匈牙利求二分图最大匹配

也可以二分,坐标的范围不大,将红蓝点分别按x坐标存y,从左开始枚举x坐标,用multiset存储当前x左边所有未匹配的红点y坐标,然后枚举当前x坐标下的蓝点的y,二分出最接近y的红点(小于y),将这两点进行匹配

 

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

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;

struct E {
    int x, y;
};

void solve() {
    int n;
    cin >> n;
    vector<E>r(n + 1), b(n + 1);
    for (int i = 1; i <= n; ++i) cin >> r[i].x >> r[i].y;
    for (int i = 1; i <= n; ++i) cin >> b[i].x >> b[i].y;
    vector<vector<int> > ve(n + 1);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (r[i].x < b[j].x && r[i].y < b[j].y) {
                ve[i].push_back(j);
            }
        }
    }
    int ans = 0;
    vector<int> st(n + 1), to(n + 1);
    auto dfs = [&](int u, auto dfs)->bool {
        for (auto v:ve[u]) {
            if (st[v]) continue;
            st[v] = 1;
            if (!to[v] || dfs(to[v], dfs)) {
                to[v] = u;
                return true;
            }
        }
        return false;
    };
    for (int i = 1; i <= n; ++i) {
        std::fill(st.begin(), st.end(), 0);
        if (dfs(i, dfs)) ans ++;
    }
    cout << ans;
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
//    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

CodeForces 1551D1s

思路:分类讨论n*m的情况,记a为水平,b为垂直

a a a a a a
           
           

奇*偶:

有一行只能被a覆盖

因为不管某一列有多少个b,这一列的b总共占偶数个,始终会剩一个,那么至少有一个a来覆盖。其余部分作为偶*偶处理

只需要判断k是否大于等于m/2,然后用m/2个a将第一行覆盖

 

a a        
a a        
           
           

偶*偶:

若要使用a,必须2个2个使用,即k为偶数个

首先对于列来说,不管a占用了多少列,最后会剩下偶数列,对b不会造成影响

对于行来说,需要保证剩下的行数为偶数,所以每次需要用2个a占两行

 

b        
b        
b        
b        

偶*奇:

某一列必须全为b,剩余部分作为偶*偶处理

对于一行来说,不管占用多少个a,最后剩下格子数一定为奇数,有一列只能由b来覆盖,所以就先将第一列全用b覆盖

对于b的数量并没有限制,直接把那一列删去即可

 

特别要判断下n为1或m为1的情况,只能由a或b覆盖

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

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;



void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    if (n == 1) {
        if (k == m / 2) cout << "YES\n";
        else cout << "NO\n";
        return ;
    }
    if (n % 2) {
        if (k >= m / 2) {
            k -= m / 2;
            n --;
        } else {
            cout << "NO\n";
            return ;
        }
    }
    if (m == 1) {
        if (k == 0) cout << "YES\n";
        else cout << "NO\n";
        return ;
    }
    if (m % 2) m --;
    if (2 * k <= n * m && k % 2 == 0) {
        cout << "YES\n";
    } else cout << "NO\n";
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

AtCoder abc123_d

也是很暴力的题,稍微想想就能懂的🤡

思路:首先三重循环肯定会暴的,那就先搞个二重把a[i]+b[j]的全部可能求出来,然后与c再进行匹配

这里注意到k其实只有3000,说明在a[i]+b[j]的集合里最多只有最大前k个是有效的,同样c里也是最多有前k个是有效的,那就分别只维护前k个就行了,最后在暴力的两重循环维护前k个就好了

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

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;



void solve() {
    int x, y, z, k;
    cin >> x >> y >> z >> k;
    vector<int> a(x), b(y), c(z);
    for (auto &v:a) cin >> v;
    for (auto &v:b) cin >> v;
    for (auto &v:c) cin >> v;
    priority_queue<int, vector<int>, greater<int> > q, qe;
    for (int i = 0; i < x; ++i) {
        for (int j = 0; j < y; ++j) {
            q.push(a[i] + b[j]);
            if (q.size() > k) q.pop();
        }
    }
    while (q.size()) {
        auto u = q.top();
        q.pop();
        for (int i = 0; i < z; ++i) {
            qe.push(u + c[i]);
            if (qe.size() > k) qe.pop();
        }
    }
    vector<int> ans;
    while (k --) {
        ans.push_back(qe.top());
        qe.pop();
    }
    std::reverse(ans.begin(), ans.end());
    for (auto v:ans) cout << v << '\n';
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
//    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

AtCoder arc078_b

CodeForces 448D

思路:打表看出来每一斜杠的最大值都大于前一斜杠,那就是要求出当前斜杠前的总个数,并且求出最靠后的那一条保证总个数小于k的斜杠,那么第k个数一定在下一斜杠...问题就是nm不同的话求斜杠前的总个数就很麻烦

最后考虑的二分,就比较好写,二分答案,统计小于等于当前数的总个数,枚举其中一个乘数就可以求出另一个乘数的个数

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

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;



void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    int l = 1, r = n * m, ans;
    auto check = [=] (int x) {
        int cnt = 0;
        for (int i = 1; i <= n; ++i) {
            cnt += min(x / i, m);
        }
        return cnt >= k;
    };
    while (l <= r) {
        int mid = l + r >> 1;
        if (check(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << ans;
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
//    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

CodeForces 1618F

思路:首先将xy转化为二进制,现在考虑如何将x转换成y

假定二进制ABCD,1表示在末尾加‘1’后翻转,0表示在末尾加‘0’后翻转

进行操作:

1 → 1DCBA

10 → ABCD1

11 → 1ABCD1

0 → DCBA (无前缀零)

01 → 1ABCD (无后缀零)

00 → ABCD (无后缀零)

可以发现,进行这些操作后的二进制数首尾都不会存在0,且可以通过这些操作在首尾加任意个‘1’

可以归纳为四种基础情况,分别为1DCBA、ABCD1、DCBA、ABCD,再分别进行首尾加‘1’与y比较即可

其次是数的范围为1e18,最多有64位,暴力枚举首尾最多放64位即可

void solve() {
    int x, y;
    cin >> x >> y;
    if (x == y) {
        cout << "YES\n";
        return ;
    }
    string a, b;
    while (x) {
        if (x % 2) a.push_back('1');
        else a.push_back('0');
        x /= 2;
    }
    while (y) {
        if (y % 2) b.push_back('1');
        else b.push_back('0');
        y /= 2;
    }
    std::reverse(a.begin(), a.end());
    std::reverse(b.begin(), b.end());

    vector<string> ve;
    //  ABCD1
    ve.push_back(a + '1');
    std::reverse(a.begin(), a.end());
    //  DCBA1
    ve.push_back('1' + a);
    std::reverse(a.begin(), a.end());
    while (a.size() && a.back() == '0') a.pop_back();
    //  ABCD(无后缀0)
    ve.push_back(a);
    std::reverse(a.begin(), a.end());
    // DCBA(无前缀0)
    ve.push_back(a);

    for (int i = 0; i < ve.size(); ++i) {
        string l;
        for (int j = 0; j <= 64; ++j) {
            if (j + ve[i].size() > b.size()) break;
            string r;
            for (int k = 0; k <= 64; ++k) {
                if (j + ve[i].size() + k > b.size()) break;
                if (l + ve[i] + r == b) {
                    cout << "YES\n";
                    return ;
                }
                r.push_back('1');
            }
            l.push_back('1');
        }
    }
    cout << "NO\n";
}

AtCoder abc265_a

思路:暴力枚举xy的个数

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

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;



void solve() {
    int x, y, n;
    cin >> x >> y >> n;
    int ans = LLONG_MAX;
    for (int i = 0; i <= n; ++i) {
        for (int j = 0; j * 3 <= n; ++j) {
            if (i + j * 3 == n) {
                ans = min(i * x + j * y, ans);
            }
        }
    }
    cout << ans;
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
//    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

AtCoder abc207_e

思路:题目说道这是一棵树,那1和n之间只有一条路径,且F和S一人得一半。那就是求所有点到1和n的最短路径,若一个点到1的最短路径小于等于到n的最短路径,那么这个点是被1选择的

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define PII pair<int, int>
const int N = 2e5 + 5, mod = 998244353;


void solve() {
    int n;
    cin >> n;
    vector<vector<int> > ve(n + 1);
    for (int i = 1; i < n; ++i) {
        int a, b;
        cin >> a >> b;
        ve[a].push_back(b), ve[b].push_back(a);
    }

    vector<array<int, 2> > dis(n + 1);
    auto bfs = [&] (int u, int op) {
        queue<int> q;
        vector<int> st(n + 1);
        q.push(u);
        dis[u][op] = 0;
        st[u] = 1;
        while (q.size()) {
            auto t = q.front();
            q.pop();
            for (auto v:ve[t]) {
                if (!st[v]) {
                    st[v] = 1;
                    dis[v][op] = dis[t][op] + 1;
                    q.push(v);
                }
            }
        }
    };
    bfs(1, 0), bfs(n, 1);
    int cnta = 0, cntb = 0;
    for (int i = 1; i <= n; ++i) {
        if (dis[i][0] <= dis[i][1]) cnta ++;
        else cntb ++;
    }
    if (cnta > cntb) cout << "Fennec";
    else cout << "Snuke";
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
//    cin >> T;
    while (T--) {
        solve();
    }

    return 0;
}

 

posted @ 2024-07-14 13:15  bible_w  阅读(40)  评论(0编辑  收藏  举报