# NOIP 2020 简要题解

## 题目链接

https://loj.ac/p?keyword=NOIP2020

## 题解

### A. 排水系统 / water

#include<bits/stdc++.h>

using namespace std;

const int N = 123456;
const long long base = 1e18;

struct bigint_t {
long long foo, bar;

bigint_t() {
foo = bar = 0;
}

bigint_t(long long foo, long long bar): foo(foo), bar(bar) {
}

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

bigint_t operator / (const int& x) {
bigint_t result;
result.foo = foo / x;
result.bar = ((foo % x) * base + bar) / x;
return result;
}

int operator % (const int& x) {
return ((foo % x) * base + bar) % x;
}

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

const bigint_t root = bigint_t(36, 279705600000000000ll); // 60^11

int n, m, degree[N];
bigint_t water[N];

int main() {
freopen("water.in", "r", stdin);
freopen("water.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
int k, x;
cin >> k;
while (k--) {
cin >> x;
++degree[x];
}
}
queue<int> q;
vector<int> nodes;
for (int i = 1; i <= n; ++i) {
if (!degree[i]) {
q.push(i);
nodes.push_back(i);
}
}
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto y : adj[x]) {
if (--degree[y] == 0) {
q.push(y);
nodes.push_back(y);
}
}
}
for (auto x : nodes) {
if (x <= m) {
water[x] = root;
}
for (auto y : adj[x]) {
water[y] = water[y] + water[x];
}
}
}
vector<int> factor(3);
factor[0] = 2;
factor[1] = 3;
factor[2] = 5;
for (int i = 1; i <= n; ++i) {
bigint_t num = water[i], den = root;
for (auto j : factor) {
while (num % j == 0 && den % j == 0) {
num = num / j;
den = den / j;
}
}
num.print();
cout << ' ';
den.print();
cout << '\n';
}
}
return 0;
}


### B. 字符串匹配 / string

#include<bits/stdc++.h>

using namespace std;

const int N = 1234567;
const int sigma = 26;

int n, fail[N], loop[N], pre[N], suf[N];
string s;

int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
cin >> s;
n = s.length();
for (int i = 1, j; i < n; ++i) {
for (j = fail[i]; j && s[j] != s[i]; j = fail[j]);
fail[i + 1] = s[j] == s[i] ? j + 1 : 0;
}
for (int i = 1; i <= n; ++i) {
if (i % (i - fail[i]) == 0) {
loop[i] = i - fail[i];
} else {
loop[i] = i;
}
}
vector<int> pool(sigma, 0);
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1];
if (++pool[s[i - 1] - 'a'] & 1) {
++pre[i];
} else {
--pre[i];
}
}
suf[n + 1] = 0;
pool = vector<int>(sigma, 0);
for (int i = n; i; --i) {
suf[i] = suf[i + 1];
if (++pool[s[i - 1] - 'a'] & 1) {
++suf[i];
} else {
--suf[i];
}
}
pool = vector<int>(sigma + 1, 0);
for (int i = 2; i < n; ++i) {
for (int j = pre[i - 1]; j <= sigma; ++j) {
++pool[j];
}
for (int j = i; j < n; j += i) {
if (i % loop[j]) {
break;
}
}
}
}
return 0;
}


### C. 移球游戏 / ball

1. 整理。对于单根柱子 $$i$$，设它上面本来应该放白球，且现在上面有 $$x$$ 个黑球，$$m - x$$ 个白球。首先任选另外一根柱子 $$j$$，将这根柱子顶部的 $$x$$ 个球放到第 $$n + 1$$ 根柱子上去。之后，在第 $$i$$ 根柱子顶部的球如果为黑色，就移动其到第 $$j$$ 根柱子上，否则移动其到第 $$n + 1$$ 根柱子上。最后，将第 $$n + 1$$ 根柱子上的 $$m - x$$ 个白球和第 $$j$$ 根柱子上的 $$x$$ 个黑球依次移回柱子 $$i$$ 上，再将第 $$n + 1$$ 根柱子上剩下的 $$x$$ 个球移回柱子 $$j$$ 上。那么在该过程结束后，柱子 $$i$$ 上的所有黑球（即不应被放在这一柱子上的球）均已位于所有白球的上端，且其余任何一根柱子上球的顺序未改变。
2. 交换。在整理完所有柱子后，设柱子 $$i$$ 上本来应该放白球，它的顶部现有 $$x$$ 个黑球；柱子 $$j$$ 上本来应该放黑球，它的顶部现有 $$y$$ 个白球。不妨令 $$x > y$$。首先将柱子 $$i$$ 上的 $$x$$ 个黑球放到第 $$n + 1$$ 根柱子上，再将柱子 $$j$$ 上的 $$y$$ 个白球放到柱子 $$i$$ 上，最后将第 $$n + 1$$ 根柱子上的所有黑球放到柱子 $$i$$$$j$$ 上。那么在该过程结束后，柱子 $$j$$ 上的球已全为黑色（颜色均已正确），柱子 $$i$$ 的顶部也只剩下 $$x - y$$ 个黑球需要继续进行交换。不断重复交换过程，直至所有柱子上的球颜色都正确。

#include<bits/stdc++.h>

using namespace std;

const int N = 56;
const int M = 456;

int n, m, a[N][M], b[N][M], top[N];

void move(int x, int y) {
a[y][top[y] + 1] = a[x][top[x]];
b[y][top[y] + 1] = b[x][top[x]];
++top[y];
--top[x];
}

void order(int x, int y, int z, int type) {
for (int i = 1; i <= z; ++i) {
move(y, n + 1);
}
for (int i = m; i >= 1; --i) {
move(x, b[x][i] == type ? y : n + 1);
}
for (int i = 1; i <= m - z; ++i) {
move(n + 1, x);
}
for (int i = 1; i <= z; ++i) {
move(y, x);
}
for (int i = 1; i <= z; ++i) {
move(n + 1, y);
}
}

void divide(int l, int r) {
if (l == r) {
return;
}
int mid = l + r >> 1;
for (int i = l; i <= r; ++i) {
top[i] = m;
for (int j = 1; j <= m; ++j) {
b[i][j] = a[i][j] > mid;
}
}
bool stop = true;
for (int i = l; i <= mid; ++i) {
int foobar = 0;
for (int j = 1; j <= m; ++j) {
if (b[i][j] == 1) {
stop = false;
++foobar;
}
}
if (foobar) {
order(i, r, foobar, 1);
}
}
for (int i = mid + 1; i <= r; ++i) {
int foobar = 0;
for (int j = 1; j <= m; ++j) {
if (b[i][j] == 0) {
++foobar;
}
}
if (foobar) {
order(i, l, foobar, 0);
}
}
if (!stop) {
int p1 = l, p2 = mid + 1;
while (1) {
while (p1 <= mid && b[p1][m] == 0) {
++p1;
}
while (p2 <= r && b[p2][m] == 1) {
++p2;
}
if (p1 == mid + 1) {
break;
}
pair<int, int> info1(p1, m), info2(p2, m);
for (int i = m; i; --i) {
if (b[p1][i] == 0) {
info1.second = m - i;
break;
}
}
for (int i = m; i; --i) {
if (b[p2][i] == 1) {
info2.second = m - i;
break;
}
}
if (info1.second < info2.second) {
swap(info1, info2);
}
for (int i = 1; i <= info1.second; ++i) {
move(info1.first, n + 1);
}
for (int i = 1; i <= info2.second; ++i) {
move(info2.first, info1.first);
}
for (int i = 1; i <= info1.second - info2.second; ++i) {
move(n + 1, info1.first);
}
for (int i = 1; i <= info2.second; ++i) {
move(n + 1, info2.first);
}
}
}
divide(l, mid);
divide(mid + 1, r);
}

int main() {
freopen("ball.in", "r", stdin);
freopen("ball.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];
}
}
divide(1, n);
for (auto p : answer) {
cout << p.first << ' ' << p.second << '\n';
}
return 0;
}


### D. 微信步数 / walk

#include<bits/stdc++.h>

using namespace std;

const int K = 13;
const int N = 567890;
const int mod = 1e9 + 7;

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

int power(int x, int y) {
int result = 1;
while (y) {
if (y & 1) {
result = mul(result, x);
}
x = mul(x, x);
y >>= 1;
}
return result;
}

struct info_t {
int l, r, p;

info_t() {
l = r = p = 0;
}

info_t(int l, int r, int p): l(l), r(r), p(p) {
}

int length() {
return r - l;
}

void modify() {
l = min(l, p);
r = max(r, p);
}

info_t operator + (const info_t& a) {
return info_t(min(l, p + a.l), max(r, p + a.r), p + a.p);
}
} pre[K][N];

int n, k, w[K], coef[K][N], value[K], inv[K], invfac[K];

int calc(int x) {
int result = 0;
for (int i = 1; i <= n; ++i) {
int foo = 1;
for (int j = 1; j <= k; ++j) {
int bar = w[j];
sub(bar, coef[j][i]);
sub(bar, mul(coef[j][n], x));
foo = mul(foo, bar);
}
}
return result;
}

int lagrange(int x) {
vector<int> pre(k + 4), suf(k + 4);
pre[0] = suf[k + 3] = 1;
for (int i = 1; i <= k + 2; ++i) {
pre[i] = mul(pre[i - 1], x - i + mod);
}
for (int i = k + 2; i; --i) {
suf[i] = mul(suf[i + 1], x - i + mod);
}
int result = 0;
for (int i = 1; i <= k + 2; ++i) {
int den = mul(invfac[i - 1], invfac[k + 2 - i]);
if (k + 2 - i & 1) {
den = mul(den, mod - 1);
}
add(result, mul(mul(value[i], den), mul(pre[i - 1], suf[i + 1])));
}
return result;
}

int main() {
freopen("walk.in", "r", stdin);
freopen("walk.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
invfac[0] = inv[1] = invfac[1] = 1;
for (int i = 2; i <= k + 1; ++i) {
inv[i] = mul(mod - mod / i, inv[mod % i]);
invfac[i] = mul(invfac[i - 1], inv[i]);
}
for (int i = 1; i <= k; ++i) {
cin >> w[i];
}
bool stop = false;
for (int i = 1; i <= n; ++i) {
int x, y;
cin >> x >> y;
for (int j = 1; j <= k; ++j) {
pre[j][i] = pre[j][i - 1];
if (j == x) {
pre[j][i].p += y;
pre[j][i].modify();
if (pre[j][i].length() != pre[j][i - 1].length()) {
foobar = mul(foobar, power(w[j], mod - 2));
foobar = mul(foobar, w[j] - 1);
if (--w[j] == 0) {
stop = true;
}
}
}
}
}
if (stop) {
return 0;
}
int infty = 0;
for (int i = 1; i <= k; ++i) {
if (!pre[i][n].p && pre[i][n].r - pre[i][n].l < w[i]) {
++infty;
}
}
if (infty == k) {
cout << -1 << '\n';
return 0;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j) {
coef[j][i] = coef[j][i - 1];
if ((pre[j][n] + pre[j][i]).length() != (pre[j][n] + pre[j][i - 1]).length()) {
++coef[j][i];
}
}
}
for (int i = 0; i <= k + 2; ++i) {
value[i] = (value[i - 1] + calc(i)) % mod;
}
int need = w[1];
for (int i = 1; i <= k; ++i) {
if (coef[i][n]) {
need = min(need, w[i] / coef[i][n]);
}
}
for (int i = 1; i <= k; ++i) {
w[i] -= need * coef[i][n];
}
if (need) {
}
foobar = 1;
for (int i = 1; i <= k; ++i) {
foobar = mul(foobar, w[i]);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j) {
if ((pre[j][n] + pre[j][i]).length() != (pre[j][n] + pre[j][i - 1]).length()) {
foobar = mul(foobar, power(w[j], mod - 2));
foobar = mul(foobar, w[j] - 1);
--w[j];
}
}