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

打了两棵线段树又慢又丑....

posted @ 2020-10-01 09:01  buzzhou  阅读(130)  评论(0编辑  收藏  举报