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 ();
}