Live2D

Solution -「牛客 31454H」Permutation on Tree

Description

  Link.

  给定一棵含有 n 个点的有根外向树, 对于所有满足树形拓扑关系的结点遍历顺序 p1..n 求出 i=2n|pi1pi| 之和. 答案对 (109+7) 取模.

  n200 n5×103.

Solution

  膜拜理论信息学家 OneInDark. 不仅踩爆标算, 还极大加速了 BI (Bunny Intelligence) 代码生成工具的开发进程.

  由于是被讲明白的, 所以可能会比较 unmotivated 呜.

  考虑枚举序列上相邻的编号 x,y, 计数它们在多少个序列中出现.

  直接表示出想要求的东西? 设 w=lca(u,v), 令 f(x,y) 表示仅考虑 w 子树内的序列, x,y 贴贴且认为 x,y 子树内结点间无序的方案数. 注意这里为了方便转移, 我们钦定了一些无序关系.

  为方便推导, 先预处理出 g(u) 表示 u 子树内的合法序列数量. 设 qu 表示 u 的父亲, su 表示 u 的子树大小, 对于边界情况, qx=qy=w 时, 有

f(x,y)=(sw2sx+sy1)(sw1sxsy{suuson(w){x,y}})uson(u){x,y}g(u).

即, 其他兄弟该怎么选怎么选; 对于 x,y 子树, 把 x,y 贴在一个位置之后只剩 sw2 个空位和 sx+sy1 个结点需要放进去.

  对于其余情况, f(x,y) 自然是从 f(qx,y)f(x,qy) 转移. 考虑从 qx 下降到 x 的过程, x 的兄弟们需要被确定顺序, 贴贴的位置继续下放没有影响, 因而

f(x,y)+(sqx+sy2sqxsx1)(sqx1sx{suuson(qx){x}})uson(qx){x}g(u)f(qx,y).

即, 选出 x 的兄弟们的位置, 选出它们之间的顺序, 各自内部再定序, 乘上转移到的状态. qy 的转移完全对称. 不过仅当 qxw 或者 qyw 时才会发生上述转移.

  问题是, 用这玩意儿其实并不方便表示答案. 先假设 w 就是全局根, 令 rw 表示 lcaw 的"答案", 那么

rw=lca(x,y)=w|xy|(sx+sy2sx1)g(x)g(y)f(x,y).

其中中间三项系数就是补上 x,y 的定序. 注意 x,y 一定是子树内最先选出来的, 而且拿去贴贴了, 所有带了一些 1 的常数.

  最后, 自然是把 rw 的信息拿到真正的树根上去:

rqu+(squ2su1)(squ1su{svvson(qu){u}})vson(qu){u}g(v)ru.

组合意义比较简单, 注意 u 子树内有一对点在贴贴, 所以大小会减小 1.

  至于那些丑陋的 \setminus, 都可以在 g(u) 的基础上修修补补 O(1) 个因子算出来. 精细处理一些需要使用的逆元, 可以做到严格的 O(n2).

  所以最奇妙的地方还是这个 f 的状态设计. 无序有序的钦定方便了后续转移.

Code

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

const int MAXN = 200, MOD = 1e9 + 7;
int n, rt, ecnt, head[MAXN + 5], fac[MAXN + 5], ifac[MAXN + 5];
int bino[MAXN + 5][MAXN + 5], ibino[MAXN + 5][MAXN + 5];
int fa[MAXN + 5], f[MAXN + 5][MAXN + 5];
int g[MAXN + 5], ig[MAXN + 5], siz[MAXN + 5], ans[MAXN + 5];
struct Edge { int to, nxt; } graph[MAXN * 2 + 5];

inline int iabs(const int u) { return u < 0 ? -u : u; }
inline int mul(const int u, const int v) { return 1ll * u * v % MOD; }
inline void subeq(int& u, const int v) { (u -= v) < 0 && (u += MOD); }
inline int sub(int u, const int v) { return (u -= v) < 0 ? u + MOD : u; }
inline void addeq(int& u, const int v) { (u += v) >= MOD && (u -= MOD); }
inline int add(int u, const int v) { return (u += v) < MOD ? u : u - MOD; }
inline int mpow(int u, int v) {
    int ret = 1;
    for (; v; u = mul(u, u), v >>= 1) ret = mul(ret, v & 1 ? u : 1);
    return ret;
}

inline void link(const int u, const int v) {
    graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
    graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
}

inline void init() {
    bino[0][0] = 1;
    rep (i, 1, n) {
        bino[i][0] = 1;
        rep (j, 1, i) bino[i][j] = add(bino[i - 1][j - 1], bino[i - 1][j]);
    }
    rep (i, 0, n) {
        ibino[i][0] = 1;
        rep (j, 1, i) ibino[i][j] = mul(ibino[i][j - 1], bino[i][j]);
        int s = mpow(ibino[i][i], MOD - 2);
        per (j, i, 1) {
            ibino[i][j] = mul(s, ibino[i][j - 1]);
            s = mul(s, bino[i][j]);
        }
    }
}

inline void getG(const int u) {
    g[u] = siz[u] = 1;
    for (int i = head[u], v; i; i = graph[i].nxt) {
        if ((v = graph[i].to) != fa[u]) {
            fa[v] = u, getG(v), siz[u] += siz[v];
            g[u] = mul(bino[siz[u] - 1][siz[v]], mul(g[u], g[v]));
        }
    }
    ig[u] = mpow(g[u], MOD - 2);
}

inline int calc(const int x, const int y, const int u) {
    int& ret = f[x][y];
    if (ret) return ret;
    if (fa[x] == u && fa[y] == u) {
        ret = mul(g[u], mul(mul(ibino[siz[u] - 1][siz[x]],
          ibino[siz[u] - siz[x] - 1][siz[y]]), mul(ig[x], ig[y])));
        ret = mul(ret, bino[siz[u] - 2][siz[x] + siz[y] - 1]);
        return ret;
    }
    if (fa[x] != u) {
        addeq(ret, mul(mul(bino[siz[fa[x]] + siz[y] - 2]
          [siz[fa[x]] - siz[x] - 1], mul(g[fa[x]], mul(ig[x],
          ibino[siz[fa[x]] - 1][siz[x]]))), calc(fa[x], y, u)));
    }
    if (fa[y] != u) {
        addeq(ret, mul(mul(bino[siz[fa[y]] + siz[x] - 2]
          [siz[fa[y]] - siz[y] - 1], mul(g[fa[y]], mul(ig[y],
          ibino[siz[fa[y]] - 1][siz[y]]))), calc(x, fa[y], u)));
    }
    return ret;
}

inline std::vector<int> solve(const int u) {
    std::vector<int> sub;
    int is = mpow(siz[u] - 1, MOD - 2);
    for (int i = head[u], v; i; i = graph[i].nxt) {
        if ((v = graph[i].to) != fa[u]) {
            fa[v] = u;
            addeq(ans[u], mul(iabs(u - v), mul(mul(siz[v], is), g[u])));

            auto&& tmp(solve(v));
            for (int x: sub) for (int y: tmp) {
                int coe = mul(iabs(x - y) << 1, mul(g[x], mul(g[y],
                  bino[siz[x] + siz[y] - 2][siz[x] - 1])));
                addeq(ans[u], mul(coe, calc(x, y, u)));
            }

            sub.reserve(sub.size() + tmp.size());
            for (int y: tmp) sub.push_back(y);
        }
    }
    return sub.push_back(u), sub;
}

inline void summary(const int u) {
    int is = mpow(siz[u] - 1, MOD - 2);
    for (int i = head[u], v; i; i = graph[i].nxt) {
        if ((v = graph[i].to) != fa[u]) {
            summary(v);
            int coe = mul(g[u], mul(bino[siz[u] - 2][siz[v] - 1],
              mul(ig[v], ibino[siz[u] - 1][siz[v]])));
            addeq(ans[u], mul(ans[v], coe));
        }
    }
}

int main() {
    // freopen("tree.in", "r", stdin);
    // freopen("tree.out", "w", stdout);

    scanf("%d %d", &n, &rt), init();
    rep (i, 2, n) { int u, v; scanf("%d %d", &u, &v), link(u, v); }
    getG(rt), solve(rt), summary(rt);
    printf("%d\n", ans[rt]);
    return 0;
}

posted @   Rainybunny  阅读(296)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2020-07-04 Solution -「CF 156D」Clues
点击右上角即可分享
微信分享提示