Codeforces Round #668 (Div. 1)

传送门

A. Balanced Bitstring

题意:
给一个01串,其中某些位置为'?',即可以为0也可以为1。
并且会给出一个 \(k\)\(k\) 是偶数,现在问是否可以使得所有长度为 \(k\) 的子串中都包含相等的0和1。

思路:
显然可以发现该串存在一个长度为 \(k\) 的循环节。
也就是说每隔 \(k\) 个位置的数一定相等,根据这一点来做就好了。

Code
// Author : heyuhhh
// Created Time : 2020/09/16 11:04:53
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    for (int i = 0; i < k; i++) {
        bool has_0 = false, has_1 = false;
        for (int j = i; j < n; j += k) {
            if (s[j] == '0') has_0 = true;
            if (s[j] == '1') has_1 = true;
        }
        if (has_0 && has_1) {
            cout << "NO" << '\n';
            return;
        }
        if (has_0) {
            for (int j = i; j < n; j += k) {
                s[j] = '0';
            }
        }
        if (has_1) {
            for (int j = i; j < n; j += k) {
                s[j] = '1';
            }
        }
    }
 
    vector<int> cnt(2);
    for (int i = 0; i < k; i++) {
        if (s[i] == '0') ++cnt[0];
        else if (s[i] == '1') ++cnt[1];
    }
    if (cnt[0] <= k / 2 && cnt[1] <= k / 2) {
        cout << "YES" << '\n';
    } else {
        cout << "NO" << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

B. Tree Tag

题意:
给定一颗 \(n\) 个点的无根树,现在有两个人分别位于结点 \(a\)\(b\)。现在第一个人每次最多能跳 \(da\) 距离,第二个人最多能跳 \(db\) 距离。
如果存在某一时刻两个人位于同一个点,那么第一个人就win;否则第二个人win。

思路:
题意就是第一个人要抓第二个人,第二个人就负责跑。
我们可以分情况来做做:

  • 如果 \(A\) 上来就能抓住显然就没了;
  • \(2\cdot da\geq L\)\(L\) 为树的直径。这显然 \(A\) 只需要跳到树的重心,之后便可跳到任意一个位置;
  • 现在一定存在死角,假设 \(B\) 位于死角,\(A\) 之后一定会跳过来,假设 \(A\) 下一步就能抓住 \(B\) 了,此时又分情况:
    • \(db\leq 2 * da\),那 \(B\) 铁定跑不掉;
    • 否则 \(B\) 还是有机会跑掉的,这个较为显然,注意现在的情况存在一个前提:\(L>da\ \&\&\ db>2*da\)

就分上面几种情况来做,还是有点细节。

Code
// Author : heyuhhh
// Created Time : 2020/09/07 09:29:10
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n, a, b, da, db;
    cin >> n >> a >> b >> da >> db;
    --a, --b;
    vector<vector<int>> G(n);
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vector<int> dep(n), up(n);
    int L = 0, rt = 0;
    function<void(int, int)> dfs = [&] (int u, int fa) {
        up[u] = fa;
        if (dep[u] > L) {
            L = dep[u];
            rt = u;
        }
        for (auto v : G[u]) if (v != fa) {
            dep[v] = dep[u] + 1;
            dfs(v, u);
        }
    };
    dfs(0, -1);
    dep[rt] = 0;
    int node = rt;
    dfs(rt, -1);
    int x = a, y = b;
    if (dep[a] < dep[b]) 
        swap(a, b);
    while (dep[a] > dep[b]) 
        a = up[a];
    while (a != b)
        a = up[a], b = up[b];
    int dis = dep[x] + dep[y] - 2 * dep[a];
    if (da >= dis || min(L, db) <= 2 * da) {
        cout << "Alice" << '\n';
    } else {
        cout << "Bob" << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C. Fixed Point Removal

题意:
给定一个序列 \(a\),现在可以执行若干次下面的操作:

  • \(a_i=i\),那么可以删除 \(a_i\),后面的元素往前移动。

然后回答若干组询问,每组询问为一段区间,表示只能删除区间 \([l,r]\) 中的数。
对于每组询问回答最多能够执行多少次上述操作。

思路:
一个较为显然的想法是令 \(a_i=i-a_i\),那么每次能够删除一个为 \(0\) 的位置,后面的元素都减去 \(1\)
如果没有询问的话那么就是一个贪心,我们每次删除最后面的那个 \(0\) 就行。
但现在有询问就比较难搞,仔细思考其实可以注意到如果固定左端点,我们不断增加右端点的话就很好做,具体来说假设当前 \([l,r]\) 的答案为 \(x\),之后右端点要增加 \(1\)。那么如果 \(0\leq a_{r+1}\leq x\)\([l,r+1]\) 的答案将变为 \(x+1\)。我们不用管是如何操作的,但是最后最优方法一定可以使得答案增加 \(1\)

但是现在有很多个区间,左端点还会移动,这样不好判断。

我们还是考虑从左往右移动右端点,用一个数组比如 \(f[i]\) 表示左端点为 \(i\) 时的答案。容易发现 \(f[1]\geq f[2]\geq\cdots\geq f[r]\)。那么根据这一点,我们右端点移动时,会有一个前缀增加 \(1\),现在就很容易通过数据结构去维护了。
现在只需将询问离线,在访问到询问区间右端点时统计答案即可。
细节见代码:

Code
// Author : heyuhhh
// Created Time : 2020/09/16 15:46:25
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = (1 << 19);
 
struct FT {
    int c[N];
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, int v) {
        for (++x; x < N; x += lowbit(x)) {
            c[x] += v;
        }
    }
    int query(int x) {
        int res = 0;
        for (++x; x; x -= lowbit(x)) {
            res += c[x];
        }
        return res;
    }
    int find(int x) {
        int p = 0;
        for (int i = 18; i >= 0; i--) {
            if (query(p | (1 << i)) >= x) {
                p |= (1 << i);
            }
        }
        return p - 1;
    }
} bit;
 
void run() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        --a[i];
        a[i] = i - a[i];
    }
 
    vector<vector<pii>> Q(n);
    for (int i = 1; i <= q; i++) {
        int x, y;
        cin >> x >> y;
        int l = x, r = n - y - 1;
        Q[r].emplace_back(l, i);
    }
    vector<int> ans(q + 1);
    for (int r = 0; r < n; r++) {
        if (a[r] >= 0) {
            int L = 0, R = r + 1, mid;
            while (L < R) {
                mid = (L + R) >> 1;
                if (bit.query(mid) >= a[r]) {
                    L = mid + 1;
                } else {
                    R = mid;
                }
            }
            // dbg(r, L);
            bit.add(0, 1), bit.add(L, -1);
        }
 
        for (auto& it : Q[r]) {
            int l = it.fi, id = it.se;
            ans[id] = bit.query(l);
        }
    }
    for (int i = 1; i <= q; i++) {
        cout << ans[i] << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

D. Game of Pairs

题意:
首先会给定一个 \(n\),然后可以选择先手或者后手:

  • 如果选择先手,将 \(1,2,\cdots,2n\) 两两分组,后手此时只能从每组里面选择一个,如果选出来的数的和是 \(2n\) 的倍数就输了,否则就赢;
  • 如果选择后手,那么会给出来一个类似于上面的分组,现在你要选出一些数,如果这些数的和为 \(2n\) 的倍数那么就赢了,否则就输。

现在就问怎么操作能够必胜。

思路:
如果 \(n\) 为偶数的话,那么类似于 \((1,n+1),(2,n+2),\cdots,(n,2n)\) 这样分组能够必胜。
原因在于所有组中的两个数在模 \(n\) 意义下就是相等的。那么后者必然选出来的数满足 \(\displaystyle 1+2+\cdots+n-1\equiv \frac{n\cdot(n-1)}{2}(mod\ n)\)
分式很难看,令 \(n=2m\),就有 \(m\cdot(2m-1)(mod\ 2m)\)。因为 \(2m-1\) 为奇数,那么式子显然不为 \(0\),也就是说不整除 \(n\),显然也不整除 \(2n\)

如果 \(n\) 为奇数的话,需要下面的观察:

  • 所有数的和为 \(n\cdot(2n+1)\),其在模 \(2n\) 意义下为\(n\)。那么如果我选出来的数在模 \(n\) 意义下为 \(0\),就能找到答案。

因为此时如果 \(\% 2n\)\(0\),就是答案;如果为 \(n\),我全部选另外的数,那就是答案。

还有另外一个观察:

  • \(\displaystyle 1+2+\cdots+n-1\equiv \frac{n\cdot(n-1)}{2}\),此时 \(n\) 为奇数,那么一定是 \(n\) 的倍数。

所以现在我们就选出一些在模 \(n\) 意义下两两不同的数即可。
这个是能够选出来的,我们连边 \(i\rightarrow i+n\),以及分组中的数互相连边,那么最终一定会形成若干个长度为偶数的环。之后对每个环奇偶染色即可。

Code
// Author : heyuhhh
// Created Time : 2020/09/16 18:33:32
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    if (n % 2 == 0) {
        cout << "First" << '\n';
        for (int i = 0; i < 2 * n; i++) {
            if (i) cout << ' ';
            cout << i % n + 1;
        }
        cout << endl << endl;
        int x;
        cin >> x;
    } else {
        cout << "Second" << endl << endl;
        vector<vector<int>> has(n);
        for (int i = 0; i < 2 * n; i++) {
            int bel;
            cin >> bel;
            --bel;
            has[bel].emplace_back(i);
        }
        vector<vector<int>> G(2 * n);
        for (int i = 0; i < n; i++) {
            G[i].emplace_back(i + n);
            G[i + n].emplace_back(i);
        }
        for (int i = 0; i < n; i++) {
            G[has[i][0]].emplace_back(has[i][1]);
            G[has[i][1]].emplace_back(has[i][0]);
        }
        vector<int> col(2 * n, -1);
        vector<int> zeros, ones;
        int res = 0;
        function<void(int, int)> go = [&] (int u, int c) {
            col[u] = c;
            if (c == 0) {
                res = (res + u + 1) % (2 * n);
                zeros.emplace_back(u);
            } else {
                ones.emplace_back(u);
            }
            for (auto& v : G[u]) {
                assert(col[v] != col[u]);
                if (col[v] == -1) {
                    go(v, c ^ 1);
                }
            }
        };
        for (int i = 0; i < 2 * n; i++) {
            if (col[i] == -1) {
                go(i, 0);
            }
        }
        if (res == 0) {
            for (int i = 0; i < sz(zeros); i++) {
                if (i) cout << ' ';
                cout << zeros[i] + 1;
            }
        } else {
            for (int i = 0; i < sz(ones); i++) {
                if (i) cout << ' ';
                cout << ones[i] + 1;
            }
        }
        cout << endl << endl;
        int x;
        cin >> x;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
posted @ 2020-10-09 20:48  heyuhhh  阅读(201)  评论(3编辑  收藏  举报