Codeforces Round 919 (Div. 2)

https://codeforces.com/contest/1920

B还行,C、E good(E据说是很典的dp但我是dp苦手),D、F1无聊,F2不会

A. Satisfying Constraints *800

\(n\) 个条件,每个条件形如 \(x\ge k,x\le k\)\(x\neq k\)\(k\) 为整数。问满足条件的整数 \(x\) 的个数。

先处理 \(\ge,\le\),得到限制区间的交集,最后把区间中 \(\neq\) 的减去。

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

void sol() {
    int n;
    cin >> n;
    int l = -1e9, r = 1e9;
    vector<int> neq;
    while (n--) {
        int t, x;
        cin >> t >> x;
        if (t == 1) l = max(l, x);
        else if (t == 2) r = min(r, x);
        else neq.push_back(x);
    }
    int ans = max(0, r - l + 1);
    for (int x : neq) ans -= x >= l && x <= r;
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);    
    int T; cin >> T; while (T--)
        sol();
}
 

B. Summation Game *1100

给定正整数数组。Alice先移除不超过 \(k\) 个数,然后Bob把不超过 \(x\) 个数取成相反数,然后游戏结束。Alice想最大化所有数的和 \(sum\),Bob想最小化 \(sum\),问 \(sum\) 最后会是多少。

Bob肯定会取前 \(x\) 大的数,为了减少损失,Alice也得移除最大的若干个数(不一定要移除 \(k\) 个)。从大到小排序数组,枚举Alice移除了几个数,前缀和算算答案即可。

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

void sol() {
    int n, k, x;
    cin >> n >> k >> x;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    sort(next(a.begin()), a.end(), greater<int>());
    for (int i = 1; i <= n; i++)
        a[i] += a[i - 1];
    
    int ans = -1e9;
    for (int i = 0; i <= k; i++) {
        int j = min(n, i + x);
        ans = max(ans, -a[j] + a[i] + a[n] - a[j]);
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);    
    int T; cin >> T; while (T--)
        sol();
}
 

C. Partitioning the Array *1600 暴露智商了,没做出来555

给定长度为 \(n\) 的数组,对 \(n\) 的每个因子 \(d\),把数组分成 \(n/d\) 个长度为 \(d\) 的连续子列,若存在 \(m\ge 2\) 使得每个连续子列在 \(\mod m\) 意义下完全相同,则答案加一。问答案是多少。

\(n\le 10^5, 1\le a_i\le n\)

转化成 \(\forall i\in[1,d]\)\(\exists m\ge 2\),使得 \(\ a_i\equiv a_{i+d}\pmod m\)

也就是 \(a_{i+d}-a_i\equiv 0\pmod m\)

也就是 \(m|(a_{i+d}-a_i)\)

\(m\) 取这些数的 \(\gcd\) 即可,也就是

\[\gcd (a_{1+d}-a_1,a_{1+2d}-a_1,a_{1+3d}-a_1,\cdots, \\a_{2+d}-a_2,a_{2+2d}-a_2,a_{2+3d}-a_2,\cdots, \\ \cdots ) \]

检查 \(\gcd\) 是否为 \(1\) 就行。\(O(\sqrt n n\log n)\),注意减法可能出现负数。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int P = 998244353;

void sol() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int &x : a) cin >> x;
    
    vector<int> divs;
    for (int i = 1; i <= n / i; i++) {
        if (n % i) continue;
        divs.push_back(i);
        if (i != n / i) divs.push_back(n / i);
    }
    
    int ans = 0;
    for (int d : divs) {
        int g = 0;
        for (int i = 0; i < d; i++)
            for (int j = i; j < n; j += d)
                g = __gcd(g, a[j] - a[i]);
        if (g != 1 && g != -1) ans++; //可能负数
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}
 

D. Array Repetition *1900 模拟

开始数组为空,两种操作:在末尾添一个数、把整个数组复制 \(x\) 次到末尾。\(q\) 次询问某个位置 \(k\) 上的数是什么。

\(n,q\le 10^5, k_i\le 10^{18}\)

模拟。注意越界处理,别爆longlong。

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

void sol() {
    int n, q;
    cin >> n >> q;
    map<ll, ll> mp;
    ll idx = 0;
    vector<ll> pos;
    for (int i = 0, f = 0; i < n; i++) {
        ll t, x;
        cin >> t >> x;
        if (f) continue;
        if (t == 1) {
            pos.push_back(++idx);
            mp[idx] = x;
        } else {
            if (idx > (ll)1e18 / (x + 1)) {
                f = true;
                continue;
            }
            idx *= x + 1;
        }
    }

    while (q--) {
        ll k;
        cin >> k;
        ll ans = mp[k];
        while (!ans) {
            ll las = *prev(upper_bound(pos.begin(), pos.end(), k));
            k = (k - 1) % las + 1;
            ans = mp[k];
        }
        cout << ans << ' ';
    }
    
    cout << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);    
    int T; cin >> T; while (T--)
        sol();
}
 

E. Counting Binary Strings *2100 得加练dp了

定义“好串”:只有一个 \(1\)\(01\) 串。

问恰有 \(n\) 个好子串且所有好子串的长度 \(\le k\)\(01\) 串数量。

\(1\le k\le n\le 2500\)

形如 000100000 的子串对答案的贡献为 \((x+1)(y+1)\),其中 \(x,y\) 分别为左右两边 \(0\) 的数量。

dp。\(f(i,j)\) 表示有 \(i\) 个好子串,最后一段 \(0\) 的长度为 \(j\) 的串的数量。(\(1\) 和总串长完全不用管)

枚举最后一段 \(0\) 的数量 \(k\),则 \(f(i,j)+=f(i-(j+1)(k+1),k)\)

注意初值 \(f(0,0)=f(0,1)=\cdots=1\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int P = 998244353;

void sol() {
    ll n, K;
    cin >> n >> K;
    vector f(n + 1, vector<ll>(K));
    fill(f[0].begin(), f[0].end(), 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < K; j++) {
            for (int k = 0; j + k + 1 <= K && i - (j + 1) * (k + 1) >= 0; k++) {
                (f[i][j] += f[i - (j + 1) * (k + 1)][k]) %= P;
            }
        }
    }
    cout << accumulate(f[n].begin(), f[n].end(), 0ll) % P << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);    
    int T; cin >> T; while (T--)
        sol();
}
 

F1. Smooth Sailing (Easy Version) *2500 好想难写的简单二分答案+bfs

给定图,每个点是海底火山/岛屿/海洋。岛屿是个四连通区域且不与边界接壤,火山和海洋都能行船。

环岛路线:由非岛屿点组成,环绕所有岛屿,使岛屿与边界不能八连通

环岛路线的安全度:路线中每个点到每座火山的曼哈顿距离的最小值。

\(q\) 次询问,问从某点出发的环岛路线的安全度最大是多少。\(q\le 5\)

二分答案 \(mid\),把起点所在的安全度 \(\ge mid\) 的非岛屿四连通区域标出,看这个区域能把不能把岛屿的边界包含在内

也就是把上述区域ban掉,看边界与岛屿是否八连通即可。代码很长

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

const int dx[] = {0,0,1,-1,1,-1,-1,1};
const int dy[] = {1,-1,0,0,1,1,-1,-1};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int n, m, q;
    cin >> n >> m >> q;
    vector g(n + 1, vector<char>(m + 1));
    queue<pair<int, int>> que;
    vector dis(n + 1, vector<int>(m + 1, -1));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> g[i][j];
            if (g[i][j] == 'v') {
                que.push({i, j});
                dis[i][j] = 0;
            }
        }
    }
    
    while (!que.empty()) { //计算每个点到最近火山的曼哈顿距离
        auto [x, y] = que.front(); que.pop();
        for (int i = 0; i < 4; i++) {
            int xx = x + dx[i], yy = y + dy[i];
            if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
            if (dis[xx][yy] != -1) continue;
            dis[xx][yy] = dis[x][y] + 1;
            que.push({xx, yy});
        }
    }
    
    while (q--) {
        int x, y;
        cin >> x >> y;
        
        auto ok = [&](int mid) {
            if (dis[x][y] < mid) return false;
            vector f(n + 1, vector<int>(m + 1)); //f=1与(x,y)四连通,f=2与边界八连通
            f[x][y] = 1;
            queue<pair<int, int>> q;
            q.push({x, y});
            while (!q.empty()) {
                auto [x, y] = q.front(); q.pop();
                for (int i = 0; i < 4; i++) {
                    int xx = x + dx[i], yy = y + dy[i];
                    if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
                    if (f[xx][yy] || dis[xx][yy] < mid || g[xx][yy] == '#') continue;
                    f[xx][yy] = 1;
                    q.push({xx, yy});
                }
            }

            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= m; j++)
                    if ((i == 1 || i == n || j == 1 || j == m) && !f[i][j])
                        q.push({i, j}), f[i][j] = 2;
            while (!q.empty()) {
                auto [x, y] = q.front(); q.pop();
                for (int i = 0; i < 8; i++) {
                    int xx = x + dx[i], yy = y + dy[i];
                    if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
                    if (g[xx][yy] == '#') return false;
                    if (f[xx][yy]) continue;
                    f[xx][yy] = 2;
                    q.push({xx, yy});
                }
            }
            return true;
        };
        
        int l = 0, r = 2e5, ans = 0;
        while (l <= r) {
            int mid = l + r >> 1;
            if (ok(mid)) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        cout << ans << '\n';
    }
    
    return 0;
}

posted @ 2024-02-08 22:14  Bellala  阅读(10)  评论(0编辑  收藏  举报