W
H
X

Codeforces Round #618 (Div. 1)

Codeforces Round #618 (Div. 1)

旧题重补系列

A

\((x|y)-y\) 就是二进制下 \(x\)\(1\)\(y\)\(0\) 的位的权值和

\(f(f(…f(f(a_1,a_2),a_3),…a_{n−1}),a_n)\) 表示二进制下只有 \(a_1\)\(1\) 其他都为 \(0\) 的位的权值和

枚举一下谁是 \(a_1\) 取个 \(max\)

B

奇数边形当然gg了...随便弄两个多边形画画发现符合条件的是中心对称的多边形

证明是有的,只是我不会,嫖一下?

C

brute:枚举 \(l\)\(1\)\(n\) 搞一遍,每次找到 \(r\) 使平均值最小,这样应该是最优的吧

性质:存在一种修改方案,每个数最多只被修改一次。

proof:如果两次修改 \([l_1,r_1],[l_2,r_2]\)(有先后顺序),\(l_1\leq l_2\leq r_1\leq r_2\),那么 \([l_2,r_2]\) 相比于 \([r_1+1,r_2]\) 多出来了 \([l_2,r_1]\) 。目的是让字典序最小,那么可以得知经过修改,\([l_2,r_1]\) 这段都变小了,既然如此,不妨直接修改 \([l_1,r_2]\)\(l_2\leq l_1\leq r_2\leq r_1\) 的情况类似。还有两个区间包含的情况当然可以只弄一次

有了性质,原问题就变成了把序列分成不相交的一些区间。可以用单调栈解决,栈中存储一些区间,如果当前数加入栈顶区间能让平均值更小就并在一起,并并并就算出来了

#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
}
#define ll long long
const int N = 1e6 + 10;
int n, a[N], num[N], tp; ll s[N];
signed main() {
    read (n);
    for (int i = 1; i <= n; ++i) read (a[i]);
    for (int i = 1; i <= n; ++i) {
        ll sum = a[i]; int cnt = 1;
        while (tp && s[tp] * cnt > sum * num[tp]) sum += s[tp], cnt += num[tp], --tp;
        s[++tp] = sum, num[tp] = cnt;
    }
    for (int i = 1; i <= tp; ++i) {
        double res = 1.0 * s[i] / num[i];
        for (int j = 1; j <= num[i]; ++j) printf ("%.9lf\n", res);
    }
    return 0;
}

D

性质:在图中随意找一棵生成树,每条非树边构成一个环,这些环的权值(边权异或)的线性基就是所有回路的线性基

暂时先不考虑包含 \(1\) 的环,把所有和 \(1\) 相连的边断开,图分成了若干个连通块,对每个连通块算出“回路线性基”。原问题就成了每个线性基可以选或不选,有多少种选法弄不出 \(0\)

本质不同的大小为 \(5\) 的线性基一共也没几种(374),先全部找出来,并预处理两种线性基合并的结果,然后一遍 \(dp\) 即可(\(f_{i,j}\) 表示处理到第 \(i\) 块,当前所选线性基合并为 \(j\)

对于包含 \(1\) 的环,在 \(dp\) 中按选不选这个环分类转移即可

#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5, M = N << 1, mod = 1e9 + 7;
struct edge { int v, w; } e[M];
int n, m, cnt = 1, h[N], nxt[M], used[M];
void add (int u, int v, int w) {
    e[++cnt] = {v, w}, nxt[cnt] = h[u], h[u] = cnt;
    e[++cnt] = {u, w}, nxt[cnt] = h[v], h[v] = cnt;
}
int tot, pw[30], id[1 << 16];
#define up(x, y) ((x += y) >= mod && (x -= mod))
struct base {
    int a[5];
    void clear () { memset (a, 0, sizeof (a)); }
    base (void) { memset (a, 0, sizeof (a)); }
    int ins (int x) {
        for (int i = 4; ~i; --i) if (x & pw[i]) {
            if (a[i]) x ^= a[i];
            else {
                a[i] = x;
                for (int j = 0; j < i; ++j) if (a[i] & pw[j]) a[i] ^= a[j];
                for (int j = i + 1; j <= 4; ++j) if (a[j] & pw[i]) a[j] ^= a[i];
                return 1;
            }
        }
        return 0;
    }
    int hsh () {
        return a[4] * pw[10] + a[3] * pw[6] + a[2] * pw[3] + a[1] * pw[1] + a[0];
    }
} b[400];
void find (base now) {
    int tmp = now.hsh ();
    if (!id[tmp]) id[tmp] = ++tot, b[tot] = now;
    else return;
    for (int i = 1; i < 32; ++i) {
        base t = now; if (t.ins (i)) find (t);
    }
}
int to[400][400];
void get () {
    for (int i = 1; i <= tot; ++i) {
        base ta = b[i];
        for (int j = i; j <= tot; ++j) {
            base tb = b[j]; bool tag = 1;
            for (int k = 0; k < 5; k++)
                if (ta.a[k]) tag &= tb.ins (ta.a[k]);
            if (tag) to[i][j] = to[j][i] = id[tb.hsh ()];
        }
    }
    for (int i = 1; i <= tot + 1; ++i)
        to[i][tot + 1] = to[tot + 1][i] = i;
}
void prework () {
    pw[0] = 1;
    for (int i = 1; i <= 20; ++i) pw[i] = pw[i - 1] << 1;
    find (*new (base)); get (); to[tot + 1][tot + 1] = tot + 1;
}
int vis[N], s[N], nn, ok, a[N], f[N][380], one[N], qwq[N], kk[N], qaq[N]; base now;
void dfs (int u, int la) {
    vis[u] = 1;
    for (int i = h[u], v; i; i = nxt[i]) {
        if (used[i]) continue; used[i ^ 1] = 1; v = e[i].v;
        if (!vis[v]) s[v] = s[u] ^ e[i].w, dfs (v, u);
        else {
            if (v == 1) qwq[nn] = s[u] ^ e[i].w, qaq[nn] = 1;
            else ok &= now.ins (s[u] ^ s[v] ^ e[i].w), kk[nn] = 1;
        }
    }
}
signed main() {
    prework (); read (n), read (m);
    for (int i = 1, u, v, w; i <= m; ++i)
        read (u), read (v), read (w), add (u, v, w);
    vis[1] = 1;
    for (int i = h[1]; i; i = nxt[i]) one[e[i].v] = i;
    for (int i = 2; i <= n; ++i) if (!vis[i] && one[i]) {
        now.clear(); ok = 1, kk[++nn] = qwq[nn] = qaq[nn] = 0;
        s[i] = e[one[i]].w, used[one[i] ^ 1] = 1; dfs (i, 1);
        a[nn] = id[now.hsh ()]; if (!kk[nn]) a[nn] = tot + 1;
        if (!ok) --nn;
    }
    f[1][tot + 1] = 1;
    for (int i = 1; i <= nn; ++i) {
        for (int j = 1; j <= tot + 1; ++j) {
            up (f[i + 1][j], f[i][j]);
            up (f[i + 1][to[j][a[i]]], f[i][j]);
            if (qaq[i]) {
                up (f[i + 1][to[j][a[i]]], f[i][j]);         // 多一种选择再加一遍
                base t = b[a[i]];
                if (!t.ins (qwq[i])) continue;
                int k = id[t.hsh ()];
                up (f[i + 1][to[j][k]], f[i][j]);
            }
        }
    }
    int res = 0;
    for (int i = 1; i <= tot + 1; ++i) up (res, f[nn + 1][i]);
    printf ("%lld\n", res);
}

E

结论1:如果已经得到了 \(1,2,3...p\)\(n-p+1,...n\) 的位置,进行 \(n-2k\) 次询问,第 \(i\) 次询问不包含 \(i\) 和已经知道的位置,回答是 \(1\) 当且仅当 \(a_i=p+1\)\(a_i=n-p\)

\(1\)\(n\) 的位置随意,如果不满足 \(a_1\leq \frac{n}{2}\) 换过来即可,然后可以用 \(1\) 去确定所有数的奇偶性,用以区分 \(p+1\)\(n-p\)。至此,我们得到了一个询问次数 \(n^2\) 的做法

结论2:可以用中国剩余定理优化解法。只要得出每个数对 \(3,5,7,8\) 取模的余数就可以算出每个数(\(3\times 5\times 7\times 8 \ge 800\))。

先按照第一步的做法弄到 \(p=4\),得到 \(1,2,3,4,n,n-1,n-2,n-3\)。对于 \(3,5,7\),只要分别找到 \(2,4,6\),种不同取模结果的组合和 \(x\) 询问就可得到 \(x\) 的结果。

但对于 \(8\) 朴素做法询问次数过多,考虑倍增优化:从\(\mod 2\) 推到\(\mod4\) 推到\(\mod 8\),比如,从 \(2\) 推到 \(4\),对于奇数和偶数分别只需询问一组即可,通过分类去掉了不可能的无意义询问,大大减少了次数

#include <bits/stdc++.h>
using namespace std;
const int N = 810;
int n, q[3], a[N], mod[N][9], p[N];
void finish () {
    if (a[1] * 2 > n)
        for (int i = 1; i <= n; ++i) a[i] = n - a[i] + 1;
    printf ("! ");
    for (int i = 1; i <= n; ++i) printf ("%d ", a[i]);
    puts (""); exit (0);
}
void flush () { putchar ('\n'); fflush (stdout); }
int get (int x, int mm) { return ((-x) % mm + mm) % mm; }
signed main() {
    int test = 1;
    // ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1, x; i <= 4 && i * 2 <= n; ++i) {
        int cnt = 0;
        for (int j = 1; j <= n && cnt < 2; ++j) {
            if (a[j]) continue;
            printf ("? %d ", n - 2 * i + 1);
            for (int k = 1; k <= n; ++k)
                if (j != k && !a[k]) printf ("%d ", k); flush ();
            cin >> x; if (x) q[++cnt] = j;
        }
        if (a[q[1]]) break;
        if (i == 1) {
            a[q[1]] = 1, a[q[2]] = n, p[1] = q[1], p[n] = q[2]; mod[q[1]][2] = 1, mod[q[2]][2] = 0;
            for (int j = 1; j <= n; ++j)
                if (!a[j]) printf ("? 2 %d %d", q[1], j), flush (), cin >> mod[j][2];
        }
        else {
            if ((i & 1) == mod[q[1]][2]) a[q[1]] = i, a[q[2]] = n - i + 1, p[i] = q[1], p[n - i + 1] = q[2];
            else a[q[1]] = n - i + 1, a[q[2]] = i, p[i] = q[2], p[n - i + 1] = q[1];
        }
    }
    if (n <= 8) finish ();

    for (int i = 1, x; i <= n; ++i) {
        if (a[i]) continue;
        printf ("? 3 %d %d %d", p[1], p[2], i); flush (); cin >> x;
        if (x) continue;
        printf ("? 3 %d %d %d", p[2], p[3], i); flush (); cin >> x;
        mod[i][3] = x ? 1 : 2;
    }
    for (int i = 1, x; i <= n; ++i) {
        if (a[i]) continue;
        printf ("? 5 %d %d %d %d %d", p[1], p[2], p[3], p[n], i); flush (); cin >> x;
        if (x) { mod[i][5] = get (n + 6, 5); continue; }
        printf ("? 5 %d %d %d %d %d", p[1], p[2], p[4], p[n], i); flush (); cin >> x;
        if (x) { mod[i][5] = get (n + 7, 5); continue; }
        printf ("? 5 %d %d %d %d %d", p[1], p[3], p[4], p[n], i); flush (); cin >> x;
        if (x) { mod[i][5] = get (n + 8, 5); continue; }
        printf ("? 5 %d %d %d %d %d", p[2], p[3], p[4], p[n], i); flush (); cin >> x;
        mod[i][5] = x ? get (n + 9, 5) : get (n, 5);
    }
    for (int i = 1, x; i <= n; ++i) {
        if (a[i]) continue;
        printf ("? 7 %d %d %d %d %d %d %d", p[1], p[2], p[3], p[n - 3], p[n - 2], p[n - 1], i); flush (); cin >> x;
        if (x) { mod[i][7] = get (3 * n, 7); continue; }
        printf ("? 7 %d %d %d %d %d %d %d", p[1], p[2], p[3], p[n - 3], p[n - 2], p[n], i); flush (); cin >> x;
        if (x) { mod[i][7] = get (3 * n + 1, 7); continue; }
        printf ("? 7 %d %d %d %d %d %d %d", p[1], p[2], p[3], p[n - 3], p[n - 1], p[n], i); flush (); cin >> x;
        if (x) { mod[i][7] = get (3 * n + 2, 7); continue; }
        printf ("? 7 %d %d %d %d %d %d %d", p[1], p[2], p[3], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
        if (x) { mod[i][7] = get (3 * n + 3, 7); continue; }
        printf ("? 7 %d %d %d %d %d %d %d", p[1], p[2], p[4], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
        if (x) { mod[i][7] = get (3 * n + 4, 7); continue; }
        printf ("? 7 %d %d %d %d %d %d %d", p[1], p[3], p[4], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
        mod[i][7] = x ? get (3 * n + 5, 7) : get (3 * n + 6, 7);
    }
    for (int i = 1, x; i <= n; ++i) {
        if (a[i]) continue;
        if (mod[i][2] == 0) {
            printf ("? 4 %d %d %d %d", p[1], p[2], p[3], i); flush (); cin >> x;
            mod[i][4] = x ? 2 : 0;
        } else {
            printf ("? 4 %d %d %d %d", p[1], p[2], p[4], i); flush (); cin >> x;
            mod[i][4] = x ? 1 : 3;
        }
    }
    for (int i = 1, x; i <= n; ++i) {
        if (a[i]) continue;
        if (mod[i][4] == 0) {
            printf ("? 8 %d %d %d %d %d %d %d %d", p[1], p[2], p[3], p[n - 3], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
            mod[i][8] = x ? 0 : 4;
        }
        if (mod[i][4] == 1) {
            printf ("? 8 %d %d %d %d %d %d %d %d", p[2], p[3], p[4], p[n - 3], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
            mod[i][8] = x ? 5 : 1;
        }
        if (mod[i][4] == 2) {
            printf ("? 8 %d %d %d %d %d %d %d %d", p[1], p[3], p[4], p[n - 3], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
            mod[i][8] = x ? 6 : 2;
        }
        if (mod[i][4] == 3) {
            printf ("? 8 %d %d %d %d %d %d %d %d", p[1], p[2], p[4], p[n - 3], p[n - 2], p[n - 1], p[n], i); flush (); cin >> x;
            mod[i][8] = x ? 7 : 3;
        }
    }
    for (int i = 1; i <= n; ++i) {
        if (a[i]) continue;
        for (int j = 1; j <= n; ++j)
            if (j % 3 == mod[i][3] && j % 5 == mod[i][5] && j % 7 == mod[i][7] && j % 8 == mod[i][8])
                { a[i] = j; break; }
    }
    finish ();
}
posted @ 2021-03-26 21:34  -敲键盘的猫-  阅读(79)  评论(0编辑  收藏  举报