『GROI-R1』 古朴而优雅
TernaryTree · · 题解
赛时所有结论都推出来了,代码没调出来(
结论 1
考虑一棵树
其中
证明:
对于每个点的所有子节点
考虑每一次遍历都优先选择当前结点
结论 2
对于任意图,遍历过程中,所有经过的边构成了一棵树。
证明:
证明其是一棵树,可分解为:
-
边集
E' 是连通的 -
点集
V 的大小为边集大小加一。
对于
对于
结论 3
对于一棵基环树,有且仅有一条边没有被遍历到。形式化地,设基环树为
证明:
由结论
结论 4
没有被遍历到的这条边一定在环上。
证明:
考虑反证。若这条边不在环上,由于基环树除环边外,剩余的每条边都是割边,故若这条边没有遍历到,
结论 5
若在原树上增加边
证明:
显然的,若基环树上一条边被去掉,剩余部分仍然连通,则这条边在环上。
考虑
结论 6
若在原树上增加边
证明:
由于我们假定
结论 7
若在原树上增加边
其中
证明:
没有遍历到的边对答案没有贡献,删去后答案不变。证毕。
结论 8
在原树上删去一条边再添加一条边使得仍然连通,则最多只有
具体地,若删去一条边
证明:
删去一条边
加上一条边同理。
总结
利用结论
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 5e5 + 1;
const int mod = 1e9 + 7;
const int maxd = 20;
typedef pair<int, int> pii;
struct edge {
int to, next;
};
int n, q;
int head[maxn];
edge e[maxn << 1];
int cnt;
void add_edge(int u, int v) {
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
int fac[maxn];
int inv[maxn];
int power(int base, int freq, int mod) {
int ans = 1, tmp = base;
while (freq) {
if (freq & 1) ans = ans * tmp % mod;
freq >>= 1;
tmp = tmp * tmp % mod;
}
return ans;
}
void init() {
fac[0] = 1;
for (int i = 1; i < maxn; i++) fac[i] = fac[i - 1] * i % mod;
inv[maxn - 1] = power(fac[maxn - 1], mod - 2, mod);
for (int i = maxn - 2; ~i; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}
int fa[maxn][maxd];
int dep[maxn];
void get_dep(int u, int depth, int fat) {
dep[u] = depth;
for (int i = head[u]; i; i = e[i].next) {
if (e[i].to != fat) {
get_dep(e[i].to, depth + 1, u);
}
}
}
void get_fa(int cur, int fat) {
fa[cur][0] = fat;
for (int i = 1; i <= log2(dep[cur]) + 1; i++) {
fa[cur][i] = fa[fa[cur][i - 1]][i - 1];
}
for (int i = head[cur]; i; i = e[i].next) {
if (e[i].to != fat) {
get_fa(e[i].to, cur);
}
}
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
while (dep[u] > dep[v]) {
u = fa[u][(int) log2(dep[u] - dep[v])];
}
if (u == v) return u;
for (int i = log2(dep[u]); i >= 0; i--) {
if (fa[u][i] != fa[v][i]) {
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][0];
}
int dist(int u, int v) {
return dep[u] + dep[v] - 2 * dep[lca(u, v)];
}
int ful = 1;
int deg[maxn];
void dfs(int u, int fa, int &mul) {
int tot = 0;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v == fa) continue;
tot++;
dfs(v, u, mul);
}
mul = mul * fac[tot] % mod;
deg[u] = tot;
}
int getans(int u, int v, int x, int y) {
int cur = ful * inv[deg[u]] % mod * inv[deg[v]] % mod * inv[deg[x]] % mod * inv[deg[y]] % mod;
deg[u]--, deg[v]--, deg[x]++, deg[y]++;
cur = cur * fac[deg[u]] % mod * fac[deg[v]] % mod * fac[deg[x]] % mod * fac[deg[y]] % mod;
deg[u]++, deg[v]++, deg[x]--, deg[y]--;
return cur;
}
int firstson(int u, int v) {
int d = dep[v] - dep[u] - 1;
for (int i = log2(d + 1); ~i; i--) {
if (d >> i & 1) v = fa[v][i];
}
return v;
}
pii getson(int rt, int u, int v) {
if (dep[u] > dep[v]) swap(u, v);
if (u == rt) {
return make_pair(v, firstson(u, v));
}
return make_pair(firstson(rt, u), firstson(rt, v));
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
init();
cin >> n >> q;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
add_edge(u, v);
add_edge(v, u);
}
get_dep(1, 0, 0);
get_fa(1, 0);
dfs(1, 0, ful);
while (q--) {
int x, y;
cin >> x >> y;
int z = lca(x, y);
if (dist(x, y) == 1) {
cout << ful << endl;
continue;
}
pii ts = getson(z, x, y);
int xt = ts.first, yt = ts.second;
int ans1 = getans(z, xt, x, y), ans2 = getans(z, yt, x, y);
cout << (ans1 + ans2) % mod << endl;
}
return 0;
}