W
H
X

Codeforces Round #614 (Div. 1)

Codeforces Round #614 (Div. 1)

AB

C

先观察规律确定dp方程式,然后记搜优化(略)

D

建出整棵树空间不太够( \(3\times10^7\) 个节点 ),只能考虑类似虚树的思想。其实只需要知道每个子树里有多少节点,然后不断移动来确定重心(略)

E

如果能确定两种字母,剩下的都是第三种。那么询问 \(CO,CH,CC,HO,OO\),花费 \(1.25\),此时已经得到了除了最后一个位置的所有 \(C\) 和除了第一个位置的所有 \(O\),也就是说,\([2,n-1]\) 的所有字符已经确定。如果 \(p_1\) 还未确定,\(p_1\) 只能是 \(O,H\),假设答案为某一个,询问一次 \([1,n-1]\) 即可得出 \(p_1\),用同样的方法询问一次 \([1,n]\) 可以得出 \(p_n\),总花费 \(1.25+\frac{1}{(n-1)^2}+\frac{1}{n^2}\)

\(n=4\) 时需要特殊处理:先依次问 \(CO,CH,CC,HO\),如果某一个存在,就用类似上面确定 \(p_1,p_n\) 的方法确定剩余两位。否则询问 \(OO\),如果 \(OO\) 存在,\(O\) 一定占满了一段长度 \(\ge2\) 的前缀(因为不存在 \(CO,HO\)),最坏的情况是只确定前面两个为 \(O\),那么一定有 \(p_3=H\),然后再问一次 \([1,n]\) 确定 \(p_4\)。如果 \(OO\) 不存在,一定有 \(p_2=p_3=H\)\(p_1=O/H,p_4=C/H\),问一次 \(HHH\) 即可全部确定

#include <bits/stdc++.h>
using namespace std;
const int N = 52;
int n, m, k[N], w[N], tag[N]; char res[N];
#define flush fflush (stdout)
#define pc putchar
void query (string s) {
    cout << "? " << s << '\n';
    flush; memset (tag, 0, sizeof (tag)); cin >> m;
    for (int i = 1; i <= m; ++i) cin >> w[i], tag[w[i]] = 1;
}
void get (string s) {
    query (s);
    for (int i = 1, x; i <= m; ++i) {
        k[x = w[i]] = k[x + 1] = 1;
        res[x] = s[0], res[x + 1] = s[1];
    }
}
void solvea () {
    get ("CH"), get ("CO"), get ("CC"), get ("OO"), get ("HO");
    for (int i = 2; i < n; ++i) if (!k[i]) res[i] = 'H';
    if (!k[1]) {
        string s = "H";
        for (int i = 2; i < n; ++i) s += res[i];
        query (s); res[1] = tag[1] ? 'H' : 'O';
    }
    if (!k[n]) {
        string s = "";
        for (int i = 1; i < n; ++i) s += res[i];
        query (s + 'H'); res[n] = tag[1] ? 'H' : 'C';
    }
}
void work () {
    int l = w[1], r = w[1] + 1; string s;
    for (int i = l - 1; i >= 1; --i) {
        s = ""; for (int j = i + 1; j <= r; ++j) s += res[j];
        query ('H' + s); if (tag[i]) { res[i] = 'H'; continue; }
        query ('O' + s); res[i] = tag[i] ? 'O' : 'C';
    }
    for (int i = r + 1; i <= n; ++i) {
        s = ""; for (int j = 1; j < i; ++j) s += res[j];
        query (s + 'H'); if (tag[1]) { res[i] = 'H'; continue; }
        query (s + 'O'); res[i] = tag[1] ? 'O' : 'C';
    }
}
void solveb () {
    get ("CH"); if (m) { work (); return; }
    get ("CO"); if (m) { work (); return; }
    get ("CC"); if (m) { work (); return; }
    get ("HO"); if (m) { work (); return; }
    get ("OO");
    if (m) {
        int pos = 0;
        for (int i = 1; i <= n; ++i)
            if (!k[i]) { pos = i; break; }
        if (!pos) return;
        if (pos == 3) res[3] = 'H';
        string s = "";
        for (int i = 1; i <= 3; ++i) s += res[i];
        query (s + 'H');
        res[n] = tag[1] ? 'H' : 'C';
    } else {
        query ("HHH"); res[2] = res[3] = 'H';
        res[1] = tag[1] ? 'H' : 'O';
        res[n] = tag[2] ? 'H' : 'C';
    }
}
signed main() {
    int T, qwq; cin >> T;
    while (T--) {
        cin >> n; memset (k, 0, sizeof (k));
        n > 4 ? solvea () : solveb ();
        pc ('!'), pc (' ');
        for (int i = 1; i <= n; ++i) pc (res[i]);
        puts (""); flush; cin >> qwq;
    }
}

F

\(a_x|a_y\),连边 \((x,y)\),形成一张 \(DAG\),对每个连通块单独处理。用计算加点方案代替删点方案。若存在 \((x,y),(y,z)\),必存在 \((x,z)\),所以一个点能不能加入只需记录“已激活”的无入边的点的集合(最大 \(15\)),然后 \(dp\) 一下,设 \(f_{i,j}\) 为激活点集为 \(i\),已经加入了 \(j\) 个点,转移分两种:一种是不改变 \(i\),选择的方案数与 \(i\) 可达点数有关;另一种是改变 \(i\),直接枚举加入点。

posted @ 2021-05-23 17:41  -敲键盘的猫-  阅读(51)  评论(0编辑  收藏  举报