Educational Codeforces Round 101


E. A Bit Similar

题意:

寻找长度为\(k\)且字典序最小的\(01\)\(t\),满足\(01\)\(s\)的所有长度为\(k\)的子串均与\(t\)至少存在一个位置有相同的字符(位置需对应)

思路:

\(s\)取反后取出所有长度为\(k\)的子串,则\(t\)除了这\(n-k+1\)个串都可以取,取字典序最小的即可

#include 
using namespace std;
typedef long long ll;
const int maxn = 1000010;
char s[maxn];
int sum[maxn], vis[maxn];
void output(int n, int k) {
    string ans = "";
    while (n) {
        if (n & 1)
            ans += '1';
        else
            ans += '0';
        n >>= 1;
    }
    while (ans.size() < k) ans += '0';
    reverse(ans.begin(), ans.end());
    cout << ans << '\n';
}
int main() {
    int t, n, k;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%s", &n, &k, s + 1);
        for (int i = 1; i <= n; i++) {
            s[i] ^= 1;
            sum[i] = sum[i - 1] + s[i] - '0';
        }
        int top;
        if (k <= 20) {
            top = min((1 << k) - 1, n - k + 2);
            for (int i = 0; i <= top; i++) vis[i] = 0;
            for (int i = 1; i <= n - k + 1; i++) {
                ll cnt = 0, base = (1 << (k - 1));
                for (int j = 0; j < k; j++) {
                    if (s[i + j] == '1')
                        cnt += base;
                    base >>= 1;
                }
                if (cnt <= top) vis[cnt] = 1;
            }
        } else {
            top = n - k + 2;
            for (int i = 0; i <= top; i++) vis[i] = 0;
            for (int i = 1; i <= n - k + 1; i++) {
                if (sum[i + k - 19] - sum[i - 1]) continue;
                ll cnt = 0, base = (1 << 19);
                for (int j = 0; j < 20; j++) {
                    if (s[i + k - 20 + j] == '1')
                        cnt += base;
                    base >>= 1;
                }
                if (cnt <= top) vis[cnt] = 1;
            }
        }
        int flag = 0;
        for (int i = 0; i <= top; i++) {
            if (!vis[i]) {
                puts("YES");
                flag = 1;
                output(i, k);
                break;
            }
        }
        if (!flag) puts("NO");
    }
    return 0;
}

F. Power Sockets

题意:

\(n\)条链,节点都是白色的,一开始有个白色的根节点,每条链可以选择一个点和已经生成的树上的一个白色节点连一条边,之后这俩节点就变成黑色,问离根节点第\(k\)近的白色节点的距离,链不必用完

思路:

链从长的开始加入,每次选择树上深度最小的白色节点与链中心相连,\(cnt[i]\)表示深度为\(i\)的白色节点数量,用线段树维护区间加,每次加完一条链要查询\(cnt\)数组前缀和第一次大于等于\(k\)的下标,在线段树上二分实现

#include 
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
typedef long long ll;
const int maxn = 200010;
ll cnt[maxn << 2], lazy[maxn << 2], sum[maxn << 2];
int l[maxn], pos, mx, ans;
void up(int rt) {
    sum[rt] = sum[ls] + sum[rs];
}
void down(int rt, int ln, int rn) {
    if (lazy[rt]) {
        lazy[ls] += lazy[rt];
        lazy[rs] += lazy[rt];
        sum[ls] += (ll)ln * lazy[rt];
        sum[rs] += (ll)rn * lazy[rt];
        lazy[rt] = 0;
    }
}
void upd(int rt, int l, int r, int v, int L, int R) {
    if (L <= l && r <= R) {
        sum[rt] += (ll)v * (r - l + 1);
        lazy[rt] += v;
        return;
    }
    int m = (l + r) >> 1;
    down(rt, m - l + 1, r - m);
    if (L <= m) upd(ls, l, m, v, L, R);
    if (R > m) upd(rs, m + 1, r, v, L, R);
    up(rt);
}
ll query(int rt, int l, int r, int L, int R) {
    if (L <= l && r <= R) {
        return sum[rt];
    }
    int m = (l + r) >> 1;
    ll ans = 0;
    down(rt, m - l + 1, r - m);
    if (L <= m)
        ans += query(ls, l, m, L, R);
    if (R > m)
        ans += query(rs, m + 1, r, L, R);
    return ans;
}
int get(int rt, int l, int r, int k) {
    if (l == r) {
        return l;
    }
    int m = (l + r) >> 1;
    down(rt, m - l + 1, r - m);
    if (query(1, 1, mx, 1, m) >= k)
        return get(ls, l, m, k);
    else
        return get(rs, m + 1, r, k);
}
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    memset(cnt, 0, sizeof(cnt));
    memset(lazy, 0, sizeof(lazy));
    cnt[1] = 1;
    for (int i = 0; i < n; ++i) scanf("%d", &l[i]);
    sort(l, l + n);
    reverse(l, l + n);
    pos = 1, mx = l[0] + 500, ans = 1e9;
    upd(1, 1, mx, 1, pos + 2, pos + 1 + (l[0] - 1) / 2);
    upd(1, 1, mx, 1, pos + 2, pos + 1 + l[0] / 2);
    int res = get(1, 1, mx, k);
    if (query(1, 1, mx, 1, res) >= k)
        ans = min(ans, res);
    for (int i = 1; i < n; i++) {
        while (query(1, 1, mx, pos, pos) == 0) pos++;
        upd(1, 1, mx, -1, pos, pos);
        upd(1, 1, mx, 1, pos + 2, pos + 1 + (l[i] - 1) / 2);
        upd(1, 1, mx, 1, pos + 2, pos + 1 + l[i] / 2);
        res = get(1, 1, mx, k);
        if (query(1, 1, mx, 1, res) >= k)
            ans = min(ans, res);
    }
    printf("%d\n", ans == 1e9 ? -1 : ans - 1);
    return 0;
}