奇觀 55pts
賽時打的 $ \Theta(n^5) $ 和 $ m = 0 $ 的特殊性質拿了55pts;
考慮正解,首先,$ CCF $ 這三個字母是可以分開維護的;
對於 $ C $,其可以看作一個連了四個點的線段,對於 $ F $,其可以看作一個連了三個點的線段在再最後分別多連兩個點;
設 $ f_{i, j} $ 表示維護一個連了 $ i $ 個點的線段,最後一個點為 $ j $ 時的方案數,有轉移:
\[
f_{i, j} = \sum_{k \ is \ connected \ to \ i}^{k \leq n} f_{i - 1, k}
\]
這東西可以字首和維護,然後我們只需維護到 $ i = 4 $ 即可;
最後 $ C $ 直接算,對於 $ F $ 列舉最後一位的數 $ j $,答案即為 $ \sum_{j = 1}^{n} f_{3, j} \times (d_j) ^ 2 $,其中 $ d_j $ 為能與 $ j $ 相連的點數;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define int long long
const long long mod = 998244353;
int n, m;
long long f[5][100005], g[5][100005];
vector<int> v[100005];
main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int x, y;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
v[x].push_back(y);
v[y].push_back(x);
}
for (int i = 1; i <= n; i++) {
v[i].push_back(0);
v[i].push_back(i);
v[i].push_back(n + 1);
sort(v[i].begin(), v[i].end());
v[i].erase(unique(v[i].begin(), v[i].end()), v[i].end());
}
for (int i = 1; i <= n; i++) {
f[1][i] = 1;
g[1][i] = g[1][i - 1] + 1;
}
for (int i = 2; i <= 4; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; k < v[j].size(); k++) {
f[i][j] += (g[i - 1][v[j][k] - 1] - g[i - 1][v[j][k - 1]] + mod) % mod;
f[i][j] = (f[i][j] + mod) % mod;
}
}
for (int j = 1; j <= n; j++) {
g[i][j] = g[i][j - 1] + f[i][j];
g[i][j] = (g[i][j] + mod) % mod;
}
}
long long ans = g[4][n] * g[4][n] % mod;
long long sum = 0;
for (int i = 1; i <= n; i++) {
long long su = v[i].size() - 2;
su = n - su;
sum = (sum + f[3][i] * su % mod * su % mod + mod) % mod;
}
ans = ans * sum % mod;
cout << ans;
return 0;
}
鐵路 60pts
賽時說不清的亂搞;
其實並不需要真的加點,只需要用並查集維護一下哪些點是被刪掉的,然後把這個連通塊連到這個新點即可;
好吧,其實原題解寫的就這麼簡潔,所以我也不想寫了
點選檢視程式碼
#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
struct sss{
int t, ne;
}e[2000005];
int h[2000005], cnt;
void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
int f[500005][25];
int fa[1000005], dep[1000005];
int id[1000005];
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
void dfs(int x, int faa) {
f[x][0] = faa;
dep[x] = dep[faa] + 1;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == faa) continue;
dfs(u, x);
}
}
int ans;
int lca(int x, int y) {
if (x == y) return x;
if (dep[x] < dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y]) x = f[x][i];
}
if (x == y) return x;
for (int i = 20; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
void w(int x, int y, int now) {
int sum = 0;
x = id[x];
y = id[y];
int lc = find(lca(x, y));
id[now] = lc;
while(find(x) != lc) {
fa[find(x)] = lc;
x = find(f[x][0]);
sum++;
}
while(find(y) != lc) {
fa[find(y)] = lc;
y = find(f[y][0]);
sum++;
}
ans -= sum;
}
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int x, y;
for (int i = 1; i <= n - 1; i++) {
cin >> x >> y;
add(x, y);
add(y, x);
}
for (int i = 1; i <= n + m; i++) {
fa[i] = i;
id[i] = i;
}
dfs(1, 0);
for (int j = 1; j <= 20; j++) {
for (int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
ans = n;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
w(x, y, n + i);
cout << ans << '\n';
}
return 0;
}
To be continued...