2023牛客寒假算法基础集训营1(11/13)

World Final? World Cup! (I)

后面不用踢当且仅当即便后面全进,也赶不上另一方,注意还能踢几次球的计算

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        string s;
        cin >> s;
        int len = s.size();
        int cnt1 = 0, cnt2 = 0, ans = -1;
        for (int i = 0; i < len; i++) {
            if (i & 1) {
                if (s[i] == '1') {
                    cnt2++;
                }
                if (cnt1 > cnt2 + 5 - (i + 1) / 2 || cnt2 > cnt1 + 5 - (i + 1) / 2) {
                    ans = i + 1;
                    break;
                }
            } else {
                if (s[i] == '1') {
                    cnt1++;
                }
                if (cnt1 > cnt2 + 5 - i / 2 || cnt2 > cnt1 + 4 - i / 2) {
                    ans = i + 1;
                    break;
                }
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

现在是,学术时间 (I)

贪心地想,只要每个教授发表自己的,引用量大于等于1即可获得1点H指数,所以统计非正数即可

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        int ans = 0;
        vector a(n + 1);
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (a[i] > 0) {
                ans++;
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

现在是,学术时间 (II)

根据数据范围发现,点一定都在第一象限内,所以讨论p点的相对位置即可

AC代码:

#include 
using namespace std;
using LL = long long;
struct point {
    long long x, y;
    point operator + (const point& p) {
        return {x + p.x, y + p.y};
    }
    point operator - (const point& p) {
        return {x - p.x, y - p.y};
    }
    double dot(const point& p) const {
        return 1.0 * x * p.x + 1.0 * y * p.y;
    }
    double cross(const point& p) const {
        return 1.0 * x * p.y - 1.0 * y * p.x;
    }
    double dist(const point& p) const {
        return sqrt(1.0 * (x - p.x) * (x - p.x) + 1.0 * (y - p.y) * (y - p.y));
    }
};
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        point a, b, o;
        o.x = 0;
        o.y = 0;
        cin >> a.x >> a.y >> b.x >> b.y;
        point c, d;
        c.x = a.x;
        c.y = 0;
        d.y = a.y;
        d.x = 0;
        double ans;
        function calc = [&](point x, point y) {
            double res = 1.0 * abs(x.x - y.x) * abs(x.y - y.y);
            return res;
        };
        if (b.x >= a.x && b.y >= a.y) {
            ans = (1.0 * a.x * a.y) / (1.0 * b.x * b.y);
        } else if (b.x < a.x && b.y >= a.y) {
            ans = max((calc(b, o) - calc(b, d)) / (calc(b, d) + 1.0 * a.x * a.y), (calc(b, c) - calc(b, a)) / (calc(b, a) + 1.0 * a.x * a.y));
        } else if (b.x >= a.x && b.y < a.y) {
            ans = max((calc(b, d) - calc(b, a)) / (calc(b, a) + 1.0 * a.x * a.y), (calc(b, o) - calc(b, c)) / (calc(b, c) + 1.0 * a.x * a.y));
        } else {
            ans = max({calc(b, o), calc(b, a), calc(b, c), calc(b, d)}) / (1.0 * a.x * a.y);
        }
        cout << fixed << setprecision(10) << ans << '\n';
    }
    return 0;
}

鸡算几何

根据题意,判断有没有操作三,就判断铁丝是否发生了镜像的变化,因为铁丝是不会发生形变的,而且ABC和DEF不一定对应,就考虑叉积判断修改C和F是否都在AB和DE线段的逆时针方向,然后再判断此时AB和DE,BC和EF长度是否相等

AC代码:

#include 
using namespace std;
using LL = long long;
struct point {
    double x, y;
    point operator + (const point& p) {
        return {x + p.x, y + p.y};
    }
    point operator - (const point& p) {
        return {x - p.x, y - p.y};
    }
    double dot(const point& p) const {
        return 1.0 * x * p.x + 1.0 * y * p.y;
    }
    double cross(const point& p) const {
        return 1.0 * x * p.y - 1.0 * y * p.x;
    }
    double dist(const point& p) const {
        return sqrt(1.0 * (x - p.x) * (x - p.x) + 1.0 * (y - p.y) * (y - p.y));
    }
};
const double eps = 1e-8;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        point a, b, c, d, e, f;
        cin >> a.x >> a.y >> b.x >> b.y >> c.x >> c.y;
        cin >> d.x >> d.y >> e.x >> e.y >> f.x >> f.y;
        point x, y, i, j;
        x.x = a.x - b.x;
        x.y = a.y - b.y;
        y.x = c.x - b.x;
        y.y = c.y - b.y;
        i.x = d.x - e.x;
        i.y = d.y - e.y;
        j.x = f.x - e.x;
        j.y = f.y - e.y;
        if (x.cross(y) < 0) {
            point z = a;
            a = c;
            c = z;
        }
        if (i.cross(j) < 0) {
            point z = d;
            d = f;
            f = z;
        }
        x.x = a.x - b.x;
        x.y = a.y - b.y;
        y.x = c.x - b.x;
        y.y = c.y - b.y;
        i.x = d.x - e.x;
        i.y = d.y - e.y;
        j.x = f.x - e.x;
        j.y = f.y - e.y;
        if (sqrt(x.dot(x)) - sqrt(i.dot(i)) > eps || sqrt(y.dot(y)) - sqrt(j.dot(j)) > eps) {
            cout << "YES\n";
        } else {
            cout << "NO\n";
        }
    }
    return 0;
}

鸡玩炸蛋人

因为图是无向图,所以先考虑没有一个炸弹的情况时,在一个联通块里的方案数是联通块大小的平方,那么答案就是所有联通块大小平方的和,如果一个联通块里存在炸弹,那么答案为存在炸弹的联通块大小的平方,如果多个联通块存在炸弹,无解

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector> G(n + 1);
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int root = -1;
    vector c(n + 1);
    vector vis(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
        if (c[i] > 0) {
            root = i;
        }
    }
    int sz = 0;
    function dfs = [&](int u) {
        for (auto v : G[u]) {
            if (!vis[v]) {
                vis[v] = true;
                sz++;
                dfs(v);
            }
        }
    };
    LL ans = 0;
    if (root == -1) {
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                vis[i] = true;
                sz = 1;
                dfs(i);
                ans = ans + 1LL * sz * sz;
            }
        }
    } else {
        vis[root] = true;
        sz = 1;
        dfs(root);
        for (int i = 1; i <= n; i++) {
            if (c[i] > 0 && !vis[i]) {
                cout << "0\n";
                return 0;
            }
        }
        ans = ans + 1LL * sz * sz;
    }
    cout << ans << '\n';
    return 0;
}

鸡格线

通过暴力跑f(x)=round(10*sqrt(x))发现,0无论跑多少次都是0,[1,99]跑有限次就达到99不再变化,[100,∞]也是跑有限次达到100后就不再变化,所以只需要存储下来非0的下标,以及多少次操作后就不再变化的操作数,操作时只修改还会变化的,否则从数组中删去,并修改所有数的和

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector a(n + 1), b, c;
    vector vis(n + 1);
    LL sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        int x = a[i];
        int cnt = 0;
        if (x >= 100) {
            while (x != 100) {
                x = round(10 * sqrt(x));
                cnt++;
            }
            b.push_back(i);
            c.push_back(cnt);
        } else if (x > 0) {
            while (x != 99) {
                x = round(10 * sqrt(x));
                cnt++;
            }
            b.push_back(i);
            c.push_back(cnt);
        }
        sum += a[i];
    }
    while (m--) {
        int op;
        cin >> op;
        if (op == 2) {
            cout << sum << '\n';
        } else {
            int l, r, k;
            cin >> l >> r >> k;
            int p = lower_bound(b.begin(), b.end(), l) - b.begin();
            int q = upper_bound(b.begin(), b.end(), r) - b.begin();
            int len = b.size();
            if (len == 0) {
                continue;
            }
            if (p == q) {
                continue;
            } else {
                vector z;
                for (int i = p; i < q; i++) {
                    c[i] -= k;
                    if (c[i] <= 0) {
                        z.push_back(i);
                        if (a[b[i]] >= 100) {
                            sum -= a[b[i]];
                            sum += 100;
                        } else {
                            sum -= a[b[i]];
                            sum += 99;
                        }
                    } else {
                        sum -= a[b[i]];
                        for (int j = 0; j < k; j++) {
                            a[b[i]] = round(10 * sqrt(a[b[i]]));
                        }
                        sum += a[b[i]];
                    }
                }
                int len1 = z.size();
                int cnt = 0;
                for (int i = 0; i < len1; i++) {
                    b.erase(z[i] + b.begin() - cnt);
                    c.erase(z[i] + c.begin() - cnt);
                    cnt++;
                }
            }
        }
    }
    return 0;
}

本题主要考察了DFS

因为这张图是完整的,所以考虑一个边有多少个凸出来的以及需要有多少凹进去的与他对应,没有恰好相等的,一定需要最后一块拼图给补上缺口

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        n *= n;
        vector a(n);
        vector cnt1(4), cnt2(4);
        for (int i = 1; i < n; i++) {
            cin >> a[i];
            for (int j = 0; j < 4; j++) {
                if (a[i][j] == '1') {
                    cnt1[j]++;
                } else if (a[i][j] == '2') {
                    cnt2[j]++;
                }
            }
        }
        int ans = 10;
        for (int i = 0; i < 4; i++) {
            if (cnt1[i] == cnt2[(i + 2) % 4]) {

            } else {
                if (cnt1[i] > cnt2[(i + 2) % 4]) {
                    ans++;
                } else {
                    ans--;
                }
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

本题竟也主要考察了DFS

根据题意可知,c是有a和b进行与、或、异或三种操作之一得到的,那么要准确猜出是和对方那种操作得到的,当且仅当他的私有数进行与运算所能得到c的所有数是另外两种运算能得到c的所有数的并集,或、异或运算同理,否则不可能准确猜出。可以分类讨论各种情况,因为a是先手,所以先讨论a,如果a=0,那么或和异或其实是一种运算,a一定赢,如果b=0,同理,如果c=0,可知a^b=0而a&b!=0,所以不构成并集,不可能猜出,如果a&c!=c,那么与是或和异或的并集,a一定能猜出,那么剩下的b一定能猜出

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) {
        int a, b, c;
        cin >> a >> b >> c;
        if (a == 0) {
            cout << "1\n";
        } else if (b == 0) {
            cout << "2\n";
        } else if (c == 0) {
            cout << "-1\n";
        } else if ((a & c) != c) {
            cout << "1\n";
        } else {
            cout << "2\n";
        }
    }
    return 0;
}

本题主要考察了dp

贪心地考虑,类似于100100100...1111放置,这样一定能保证坏区间是最小的

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    string s = "";
    for (int i = 1; i <= n; i++) {
        if (n - i >= m) {
            if (i % 3 == 1) {
                s += '1';
                m--;
            } else {
                s += '0';
            }
        } else {
            while (m) {
                s += '1';
                m--;
            }
            break;
        }
    }
    int ans = 0;
    for (int i = 0; i < n - 2; i++) {
        int cnt = (s[i] == '1' ? 1 : 0) + (s[i + 1] == '1' ? 1 : 0) + (s[i + 2] == '1' ? 1 : 0);
        if (cnt >= 2) {
            ans++;
        }
    }
    cout << ans << '\n';
    return 0;
}

本题主要考察了运气

因为问团队和问第几个人相对独立,所以分别考虑他们两个各自期望操作数是多少

问团队:一次问出,两次问出,三次问出,四次问出,四次问出,最多需要四次就能判断出,那么期望就是(1+2+3+4+4)/5

问第几个人:一次问出,两次问出,三次问出,三次问出,最多需要三次就能判断出,那么期望就是(1+2+3+3)/4

两者加起来2.8+2.25=4.05,答案为32

AC代码:

32

本题主要考察了找规律

01背包,n个人背包容量为m,dp[i][j]就可以表示为前i个人占用j个空间时最大价值,因为题目没有给出物品体积,即每次需要枚举[0,m]所有体积,如果不选dp[i][j]=dp[i-1][j],如果选,首先占用的空间不能超过背包目前占用空间j,并且此时手里有的仙贝数要大于0,此时(这次还没分配)手里的仙贝数就是(m-j+所选物品的体积),所以产生的价值就是 所选物品体积/此时(这次还没分配)手里的仙贝数

AC代码:

#include 
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector> dp(n + 1, vector (m + 1));
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            dp[i][j] = dp[i - 1][j];
            for (int k = 0; k <= m; k++) {
                if (j >= k && m - j + k > 0) {
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - k] + 1.0 * k / (m - j + k));
                }
            }
        }
    }
    double ans = 0.0;
    for (int i = 0; i <= m; i++) {
        ans = max(ans, dp[n][i]);
    }
    cout << fixed << setprecision(10) << ans << '\n';
    return 0;
}

你可能感兴趣的:(寒假训练营,算法,c++,动态规划)