JZOJ 3978. 寝室管理
Description
给定一棵由n个点组成的树,或者一棵由n个点组成的有且仅有一个环的“树”。
求经过不少于k个点的路径数。
Data Constraint
\(n \leq 100000\),\(k \leq n\)
Solution
树的情况
点分治即可。
处理方式有两种:
Method1
对于一个分治中心\(u\),先把\(u\)以下的所有点抽出来按深度排序,
那么对于排序后的一个点\(i\),可以用指针在线性时间复杂度内找到最小的满足条件的点\(j\),
点\(i\)的贡献就是\(siz[u] - j\)。
但是对于\(u\)的某个儿子\(v\),以\(v\)为根的树会算重,所以可以把以\(v\)为根树内算重的数计算出来减去,
同样用指针就可以解决,复杂度一样是线性的。
Method2
遍历分治中心 \(u\) 的每棵子树,对于深度 \(d\),能与它匹配的点的深度就在 \(k - d + 1\) ~ \(n\)(设 \(u\) 的深度为1),
用一棵线段树就很好维护了。
带一个环的情况
先删掉一条环边,然后做一遍点分治。
那么还需要加上一定经过这条环边的代价。
假设环是这样子的:
蓝色边就是删去的环边,现在需要计算一定经过它的路径数。
具体方法很简单,先将环点1及子树加入数据结构(1的深度为1),然后从 \(tot\) ~ \(2\) 做。
对于点 \(i\),令它的深度为 \(i\),然后遍历 \(i\) 及 \(i\) 的子树,统计方法类似“Method2”。
然后令 \(i\) 的深度为 \(tot - i + 2\),将 \(i\) 及 \(i\) 的子树加入数据结构维护即可。
路径走出来可能类似这样:
灵魂画手了...
思路很简单不赘述正确性。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100000
#define L 100000
#define fo(i, x, y) for(ll i = x; i <= y; i ++)
#define Fo(i, u) for(ll i = head[u]; i; i = edge[i].next)
#define lson (t << 1)
#define rson (t << 1 | 1)
#define Mes(a, x) memset(a, x, sizeof a)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define ll long long
void read(ll &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
}
struct EDGE { ll next, to, bz; } edge[N << 2];
ll head[N + 1], cir[N + 1], q[N + 1], siz[N + 1], c[N + 1], vis[N + 1], In[N + 1], used[N + 1];
ll f[N << 3], g[N << 3], now[N << 3];
ll last[N + 1];
ll n, m, K, tot = 0, cnt = 0;
ll cnt_edge = 1;
void Add(ll u, ll v) { edge[ ++ cnt_edge ] = (EDGE) { head[u], v, 0 }, head[u] = cnt_edge; }
void Link(ll u, ll v) { Add(u, v), Add(v, u); }
ll tot_h = 0;
void Dfs1(ll u, ll la) {
siz[u] = c[u] = 1, q[ ++ tot_h ] = u;
Fo(i, u) if (i != la && ! used[edge[i].to] && ! edge[i].bz) {
Dfs1(edge[i].to, i ^ 1);
siz[u] += siz[edge[i].to];
if (siz[edge[i].to] > c[u])
c[u] = siz[edge[i].to];
}
}
ll Find_heart(ll u, ll la) {
tot_h = 0;
Dfs1(u, la);
fo(i, 1, tot_h) if (siz[u] - siz[q[i]] > c[q[i]])
c[q[i]] = siz[u] - siz[q[i]];
ll heart = q[1];
fo(i, 2, tot_h) if (c[q[i]] < c[heart])
heart = q[i];
return heart;
}
ll Now = 0;
void Updata1(ll t, ll l, ll r) { if (now[t] < Now) g[t] = 0, now[t] = Now; }
void Add1(ll t, ll l, ll r, ll k) {
Updata1(t, l, r);
++ g[t];
if (l == r) return;
ll mid = l + r >> 1;
k <= mid ? Add1(lson, l, mid, k) : Add1(rson, mid + 1, r, k);
}
ll Max(ll x, ll y) { return x > y ? x : y; }
ll Min(ll x, ll y) { return x < y ? x : y; }
ll Get1(ll t, ll l, ll r, ll x, ll y) {
Updata1(t, l, r);
if (x <= l && r <= y) return g[t];
ll mid = l + r >> 1;
return (x <= mid ? Get1(lson, l, mid, x, y) : 0) + (y > mid ? Get1(rson, mid + 1, r, x, y) : 0);
}
ll Dfs2(ll u, ll la, ll dep) {
ll sum = Get1(1, 1, n, Max(1, K - dep + 1), n);
Fo(i, u) if (i != la && ! used[edge[i].to] && ! edge[i].bz)
sum += Dfs2(edge[i].to, i ^ 1, dep + 1);
return sum;
}
void Dfs5(ll u, ll la, ll dep) {
Fo(i, u) if (i != la && ! used[edge[i].to] && ! edge[i].bz)
Dfs5(edge[i].to, i ^ 1, dep + 1);
Add1(1, 1, n, Min(dep, n));
}
ll Dfz(ll u, ll la) {
used[u] = 1;
++ Now; ll sum = 0;
Add1(1, 1, n, 1);
Fo(i, u)
if (i != la && ! used[edge[i].to] && ! edge[i].bz) {
sum += Dfs2(edge[i].to, i ^ 1, 2);
Dfs5(edge[i].to, i ^ 1, 2);
}
Fo(i, u) if (i != la && ! used[edge[i].to] && ! edge[i].bz)
sum += Dfz(Find_heart(edge[i].to, i ^ 1), i ^ 1);
return sum;
}
int du = 0;
ll Dfs3(ll u, ll la) {
if (vis[u]) {
cir[ ++ tot ] = u;
while (q[tot_h] != u)
cir[ ++ tot ] = q[tot_h], -- tot_h;
return 1;
}
vis[u] = 1, q[ ++ tot_h ] = u;
Fo(i, u) if (i != la) {
if (vis[edge[i].to]) du = i;
if (Dfs3(edge[i].to, i ^ 1))
return 1;
}
-- tot_h;
return 0;
}
void Add2(ll t, ll l, ll r, ll k, ll add) {
f[t] += add;
if (l == r) return;
ll mid = l + r >> 1;
k <= mid ? Add2(lson, l, mid, k, add) : Add2(rson, mid + 1, r, k, add);
}
ll Get2(ll t, ll l, ll r, ll x, ll y) {
if (x <= l && r <= y) return f[t];
ll mid = l + r >> 1;
return (x <= mid ? Get2(lson, l, mid, x, y) : 0) + (y > mid ? Get2(rson, mid + 1, r, x, y) : 0);
}
ll Dfs4(ll u, ll la, ll dep) {
ll sum = 0;
sum = Get2(1, 1, L, Max(1, K - dep + 1), L);
Fo(i, u) if (i != la && ! In[edge[i].to])
sum += Dfs4(edge[i].to, i ^ 1, dep + 1);
return sum;
}
void Dfs6(ll u, ll la, ll dep) {
Add2(1, 1, L, dep, 1);
Fo(i, u) if (i != la && ! In[edge[i].to])
Dfs6(edge[i].to, i ^ 1, dep + 1);
}
int main() {
read(n), read(m), read(K);
for (ll i = 1, x, y; i <= m; i ++)
read(x), read(y), Link(x, y);
if (n > m) {
ll ans = Dfz(Find_heart(1, 0), 0);
printf("%lld\n", ans);
} else {
tot_h = 0; Dfs3(1, 0);
edge[du].bz = edge[du ^ 1].bz = 1;
ll ans = Dfz(Find_heart(1, 0), 0);
fo(i, 1, tot) In[cir[i]] = 1;
Add2(1, 1, L, 1, 1);
Fo(j, cir[1]) if (! In[edge[j].to]) {
Dfs6(edge[j].to, j ^ 1, 2);
}
fd(i, tot, 2) {
ans += Get2(1, 1, L, Max(1, K - i + 1), L);
Fo(j, cir[i]) if (! In[edge[j].to]) {
ans += Dfs4(edge[j].to, j ^ 1, i + 1);
}
Fo(j, cir[i]) if (! In[edge[j].to])
Dfs6(edge[j].to, j ^ 1, 2 + (tot - i + 1));
Add2(1, 1, L, 1 + (tot - i + 1), 1);
}
printf("%lld\n", ans);
}
return 0;
}
打了两棵线段树又慢又丑....