# CSP-S 2019 简要题解

## 题目链接

https://loj.ac/p?keyword=CSP-S%202019

## 题解

### A. 格雷码 / code

#include<bits/stdc++.h>

using namespace std;

int n;
unsigned long long k;

int main() {
freopen("code.in", "r", stdin);
freopen("code.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
--n;
while (~n) {
cout << ((k >> n) & 1);
if (k >= 1ull << n) {
k = (1ull << n) - 1 + (1ull << n) - k;
}
--n;
}
cout << '\n';
return 0;
}


### B. 括号树 / brackets

#include<bits/stdc++.h>

using namespace std;

const int N = 567890;

class my_array {
int a[N << 1];

public:
int& operator [] (int x) {
return a[N + x];
}
} pool, ban;

int n, a[N], father[N];
long long result[N];

void dfs(int x) {
a[x] += a[father[x]];
int w = a[x], pre = ban[w];
ban[w] = pool[w + 1];
result[x] = pool[w] - ban[w - 1] + result[father[x]];
++pool[w];
for (auto y : adj[x]) {
dfs(y);
}
--pool[w];
ban[w] = pre;
}

int main() {
freopen("brackets.in", "r", stdin);
freopen("brackets.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
string brackets;
cin >> brackets;
for (int i = 0; i < n; ++i) {
a[i + 1] = brackets[i] == '(' ? 1 : -1;
}
for (int i = 2; i <= n; ++i) {
cin >> father[i];
}
++pool[0];
dfs(1);
for (int i = 1; i <= n; ++i) {
}
return 0;
}


### C. 树上的数 / tree

1. $$(x, p_1)$$ 为所有与节点 $$x$$ 相连的边中最早被删除的
2. $$(p_k, y)$$ 为所有与节点 $$y$$ 相连的边中最晚被删除的
3. 对于上述路径上的任意中间节点 $$p_i$$，路径中与该节点有关的两条边一定在所有与该节点相连的边中被连续删除

1. 由若干条链组成
2. 无连入至起始点的边
3. 无由终点连出的边

#include<bits/stdc++.h>

using namespace std;

const int N = 6789;

int n, p, id_cnt, a[N], id[N][N], father[N], size[N], degree[N][2]; // 0: in, 1: out
bool used[N];

int find(int x) {
return father[x] == x ? x : father[x] = find(father[x]);
}

void merge(int x, int y) { // x->y
++degree[x][1];
++degree[y][0];
x = find(x);
y = find(y);
father[x] = y;
size[y] += size[x];
}

void dfs(int x, int f) {
if (x != f && !used[x]) {
int y = id[x][f];
bool legal = true;
if (degree[x][0] || degree[y][1]) {
legal = false;
}
if (find(x) == find(y) && size[find(x)] <= adj[x].size()) {
legal = false;
}
if (legal) {
p = min(p, x);
}
}
for (auto y : adj[x]) {
if (y != f) {
int a = x == f ? x : id[x][f], b = id[x][y];
if (degree[b][0] || degree[a][1]) {
continue;
}
if (find(a) == find(b) && size[find(a)] <= adj[x].size()) {
continue;
}
dfs(y, x);
}
}
}

void dfs_path(int x, int goal, int f = 0) {
pool.push_back(x);
if (x == goal) {
path = pool;
return;
}
for (auto y : adj[x]) {
if (y != f) {
dfs_path(y, goal, x);
}
}
pool.pop_back();
}

int get(int x) {
p = n + 1;
dfs(x, x);
used[p] = true;
pool.clear();
dfs_path(x, p);
int m = path.size();
merge(path[0], id[path[0]][path[1]]);
for (int i = 0; i + 2 < m; ++i) {
int a = path[i], b = path[i + 1], c = path[i + 2];
merge(id[b][a], id[b][c]);
}
merge(id[path[m - 1]][path[m - 2]], path[m - 1]);
return p;
}

int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
if (n == 1) {
cout << 1 << '\n';
continue;
}
for (int i = 1; i <= n; ++i) {
}
for (int i = 1; i < n; ++i) {
int x, y;
cin >> x >> y;
}
id_cnt = n;
for (int i = 1; i <= n; ++i) {
for (auto j : adj[i]) {
id[i][j] = ++id_cnt;
}
}
for (int i = 1; i <= id_cnt; ++i) {
father[i] = i;
size[i] = 1;
}
memset(used, false, sizeof used);
memset(degree, 0, sizeof degree);
for (int i = 1; i <= n; ++i) {
cout << get(a[i]) << " \n"[i == n];
}
}
return 0;
}


### D. Emiya 家今天的饭 / meal

#include<bits/stdc++.h>

using namespace std;

const int N = 123;
const int M = 2345;
const int mod = 998244353;

void add(int& x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
}

void sub(int& x, int y) {
x -= y;
if (x < 0) {
x += mod;
}
}

int mul(int x, int y) {
return (long long)x * y % mod;
}

class my_array {
int a[M];

public:
void reset() {
memset(a, 0, sizeof a);
}

int& operator [] (int x) {
return a[(M >> 1) + x];
}
} f[2];

int n, m, a[N][M], s[N];

int dp(int ban) {
f[0].reset();
f[0][0] = 1;
for (int i = 1; i <= n; ++i) {
f[i & 1].reset();
int p = a[i][ban], other = (s[i] - a[i][ban] + mod) % mod;
for (int j = -i; j <= i; ++j) {
f[i & 1][j] = f[i - 1 & 1][j];
add(f[i & 1][j], mul(f[i - 1 & 1][j - 1], p));
add(f[i & 1][j], mul(f[i - 1 & 1][j + 1], other));
}
}
int result = 0;
for (int i = 1; i <= n; ++i) {
}
return result;
}

int main() {
freopen("meal.in", "r", stdin);
freopen("meal.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; ++i) {
}
for (int i = 1; i <= m; ++i) {
}
return 0;
}


### E. 划分 / partition

$$f_i$$ 为对前 $$i$$ 个数进行划分得到的最小平方和，$$g_i$$ 为对前 $$i$$ 个数进行最优划分时倒数第二段的右端点位置（即最后一段对应的区间为 $$(g_i, i]$$），那么有如下两条结论（记原序列 $$\{a_i\}$$ 的前缀和为 $$\{s_i\}$$）：

1. $$f_i = \min\limits_{j < i} \{f_j + (s_i - s_j)^2\}$$，其中位置 $$j$$ 只需满足 $$s_i - s_j \geq s_j - s_{g_j}$$，即一定将 $$(g_j, j]$$ 划为一段。
2. 在结论 1 中，记满足 $$s_i - s_j \geq s_j - s_{g_j}$$$$j$$ 的最大值为 $$j'$$，那么 $$j'$$$$f_i$$ 的最优转移点，即 $$g_i = j'$$

#include<bits/stdc++.h>

using namespace std;

const int N = 40000005;
const long long base = 1e18;
const long long pbase = 1e9;
const long long mod = 1 << 30;

int n, type, a[N], b[N], q[N], best[N];
long long s[N];

struct bigint_t {
long long foo, bar;

bigint_t() {
foo = bar = 0;
}

bigint_t operator + (const bigint_t& a) {
bigint_t result;
result.foo = foo + a.foo;
result.bar = bar + a.bar;
if (result.bar >= base) {
++result.foo;
result.bar -= base;
}
return result;
}

void print() {
if (foo) {
cout << foo;
cout << setw(18) << setfill('0') << bar;
} else {
cout << bar;
}
cout << '\n';
}
};

bigint_t square(long long a) {
long long x = a / pbase, y = a % pbase;
bigint_t result;
result.foo = x * x + 2 * x * y / pbase;
result.bar = 2 * x * y % pbase * pbase + y * y;
if (result.bar >= base) {
result.foo += result.bar / base;
result.bar %= base;
}
return result;
}

int main() {
freopen("partition.in", "r", stdin);
freopen("partition.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> type;
if (type == 0) {
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
} else {
int x, y, z, m, p, l, r;
cin >> x >> y >> z >> b[1] >> b[2] >> m;
int last = 0;
for (int i = 3; i <= n; ++i) {
b[i] = ((long long)x * b[i - 1] + (long long)y * b[i - 2] + z) & mod - 1;
}
for (int i = 1; i <= m; ++i) {
cin >> p >> l >> r;
for (int j = last + 1; j <= p; ++j) {
a[j] = l + (b[j] % (r - l + 1));
}
last = p;
}
}
for (int i = 1; i <= n; ++i) {
s[i] = s[i - 1] + a[i];
}
int l = 1, r = 1;
for (int i = 1; i <= n; ++i) {
auto get = [&] (int p) {
return (s[p] << 1) - s[best[p]];
};
while (l < r && s[i] >= get(q[l + 1])) {
++l;
}
best[i] = q[l];
while (l <= r && get(i) <= get(q[r])) {
--r;
}
q[++r] = i;
}
int p = n;
while (p) {
p = best[p];
}
return 0;
}


### F. 树的重心 / centroid

#include<bits/stdc++.h>

using namespace std;

const int N = 345678;
const int maxlog = 19;

int n, size[N], father[N], heavy[N][2], go[N][maxlog];

void reset(int x) {
for (int i = 1; i < maxlog; ++i) {
go[x][i] = go[go[x][i - 1]][i - 1];
}
}

void dfs_init(int x, int f = 0) {
size[x] = 1;
heavy[x][0] = heavy[x][1] = 0;
int maxs = 0;
for (auto y : adj[x]) {
if (y != f) {
father[y] = x;
dfs_init(y, x);
size[x] += size[y];
if (size[y] > size[heavy[x][0]]) {
heavy[x][1] = heavy[x][0];
heavy[x][0] = y;
} else if (size[y] > size[heavy[x][1]]) {
heavy[x][1] = y;
}
}
}
go[x][0] = heavy[x][0];
reset(x);
}

void check(int x, int all) {
int start = x;
for (int i = maxlog - 1; ~i; --i) {
int y = go[x][i];
if (y && all - size[y] <= (all >> 1)) {
x = y;
}
}
if (x != start) {
int y = father[x];
if (size[x] <= (all >> 1) && all - size[y] <= (all >> 1)) {
}
}
}

void dfs(int x, int f = 0) {
if (f) {
check(x, size[x]);
check(f, size[f]);
}
for (auto y : adj[x]) {
if (y != f) {
father[x] = y;
int foobar = size[x];
size[x] = n - size[y];
if (y == heavy[x][0]) {
go[x][0] = size[f] > size[heavy[x][1]] ? f : heavy[x][1];
} else if (size[f] > size[heavy[x][0]]) {
go[x][0] = f;
}
reset(x);
dfs(y, x);
father[x] = f;
size[x] = foobar;
go[x][0] = heavy[x][0];
reset(x);
}
}
}

int main() {
freopen("centroid.in", "r", stdin);
freopen("centroid.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
cin >> n;
for (int i = 1; i <= n; ++i) {
}
for (int i = 1; i < n; ++i) {
int x, y;
cin >> x >> y;