[WC2019] 数树

Statement

有\(n\)个节点, 分别用红线,蓝线连成两棵树. 用\(y\)种颜色给节点染色, 规定如果一条边在两棵树中同时出现, 那么边两端的点的颜色必须相同.

  • Task #1: 给定两棵树, 求染色方案.
  • Task #2: 给定其中一棵树, 求对于另一棵树的每一种形态的染色方案数之和.
  • Task #3: 两棵树的形态都没有确定, 求对于所有情况的染色方案数之和.

(\(n\le 10^5\))

Solution

Task #1

计算有多少条公共边即可.

Task #2

如果有\(i\)条公共边, 那么贡献为\(y^{n-i}\). 设\(z=y^{-1}\), 贡献为\(y^n z^{i}\), 考虑计算\(z^i\),最后乘上\(y^n\).
\[
\begin{aligned}
z^i &= (z-1+1)^i \&= \sum_{j=0}^i (z-1)^j \binom{i}{j}
\end{aligned}
\]
于是我们对于边集\(E\)的每个子集\(E'\)的贡献为\((z-1)^{|E'|}\).

对于一个公共边的边集的选取方案, 将边连起来, 形成了很多个连通块, 大小为\(a_1\dots a_m\), 那么贡献为:
\[
(z-1)^{n-m} n^{m-2} \prod_{i=1}^m a_i
\]
\((z-1)^{n-m}\)为贡献, \(n^{m-2}\prod_{i=1}^m a_i\)为\(n\)个点,\(m\)个块的树的个数.

上面这个式子的组合意义为在每个块中选一个点的方案数, 树形DP即可, 时间复杂度\(O(n)\).

Task #3

类似Task2的思路, 枚举至少有\(n-m\)条公共边, 即\(m\)个连通块的方案数:
\[
\begin{aligned}
g_m
&=
\sum_{\sum\limits_{i=1}^m a_i = n}
\sum_{j=1}^m \binom{\sum_{k=1}^j a_k - 1}{a_j-1}
\left(n^{m-2} \prod_{i=1}^m a_i\right)^2
\prod _{i=1}^m a_i ^ {a_i - 2} \&=
\sum_{\sum\limits_{i=1}^m a_i = n}
\sum_{j=1}^m \binom{\sum_{k=1}^j a_k - 1}{a_j-1}
n^{2(m-2)}
\prod_{i=1}^m a_i^{a_i}
\end{aligned}
\]

\[
\begin{aligned}
ans &= \sum_m g_{m} \times (z - 1)^{n-m} \&=
\sum_{\sum\limits_{i=1}^m a_i = n}
\sum_{j=1}^m \binom{\sum_{k=1}^j a_k - 1}{a_j-1}
n^{2(m-2)}
\prod_{i=1}^m a_i^{a_i}
(z-1)^{n-m}\\end{aligned}
\]

设\(f_k\)表示当前已经处理了\(k\)个块, 那么有转移:
\[
f_k =
\begin{cases}
n^{-4}(z-1)^n
&,k=0 \(z-1)^{-1} n^2\sum_{i=1}^k f_{k-i} \binom{k-1}{i-1} i^i
&,k=1
\end{cases}
\]
写成卷积的形式:
\[
f_k = (z-1)^{-1}(k-1)!n^2 \sum_{i=1}^k \frac{f_{k-i}}{(k-i)!}\times\frac{i^i}{(i-1)!}
\]
分治FFT即可, 时间复杂度\(O(n\log^2n)\).

namespace Subtask0 {
    map<LL, int> mp;
    void Main() {
        For(i, 1, n - 1) {
            int x = read<int>(), y = read<int>();
            mp[(LL)x * 1e9 + y] = 1;
        }
        int ans = 0;
        For(i, 1, n - 1) {
            int x = read<int>(), y = read<int>();
            if (mp[(LL)x * 1e9 + y]) ++ ans;
        }
        printf("%d\n", fpm(y, n - ans));
    }
}

namespace Subtask1 {
    int beg[MAXN], v[MAXN << 1], nex[MAXN << 1], e = 1;
    void add(int uu, int vv) {
        v[++ e] = vv, nex[e] = beg[uu], beg[uu] = e;
    }
    int dp[MAXN][2];
    void DFS(int u, int pa) {
        dp[u][0] = (LL)fpm(n, MOD - 2) * fpm(z - 1, n - 1) % MOD;
        for (int i = beg[u]; i; i = nex[i])
            if (v[i] != pa) {
                DFS(v[i], u);
                int t0 = dp[u][0], t1 = dp[u][1];
                dp[u][0] = (LL)t0 * dp[v[i]][1] % MOD * n % MOD * n % MOD * fpm(fpm(z - 1), n) % MOD;
                dp[u][1] = (LL)t1 * dp[v[i]][1] % MOD * n % MOD * n % MOD * fpm(fpm(z - 1), n) % MOD;
                (dp[u][0] += (LL)t0 * dp[v[i]][0] % MOD * n % MOD * fpm(fpm(z - 1), n - 1) % MOD) %= MOD;
                (dp[u][1] += (LL)t0 * dp[v[i]][1] % MOD * n % MOD * fpm(fpm(z - 1), n - 1) % MOD) %= MOD;
                (dp[u][1] += (LL)t1 * dp[v[i]][0] % MOD * n % MOD * fpm(fpm(z - 1), n - 1) % MOD) %= MOD;
            }
        (dp[u][1] += dp[u][0]) %= MOD;
    }
    void Main() {
        if (y == 1) {
            printf("%d\n", fpm(n, n - 2));
            return;
        }
        For(i, 2, n) {
            int u = read<int>(), v = read<int>();
            add(u, v), add(v, u);
        }
        DFS(1, 0);
        printf("%lld\n", (LL)fpm(y, n) * dp[1][1] % MOD);
    }
}

namespace Subtask2 {
    int f[MAXN << 1];
    void cdqFFT(int l, int r) {
        static int A[MAXN << 1], B[MAXN << 1];
        if (l == r) {
            if (!l) f[0] = (LL)fpm(fpm(n), 4) * fpm(z_, n) % MOD;
            else f[l] = (LL)f[l] * iz_ % MOD * fac[l - 1] % MOD;
            return;
        }
        int mid = (l + r) >> 1, len = 1, pt = 0;
        cdqFFT(l, mid);
        while (len < mid - l + 1) ++ pt, len <<= 1;
        Rep(i, len) {
            A[i] = (LL)f[l + i] * ifac[l + i] % MOD;
            if (i) B[i] = (LL)fpm(i, i) * ifac[i - 1] % MOD * n % MOD * n % MOD;
        }
        For(i, len, len * 2 - 1) {
            A[i] = 0;
            B[i] = (LL)fpm(i, i) * ifac[i - 1] % MOD * n % MOD * n % MOD;
        }
        calcrev(len << 1, pt + 1);
        NTT(A, len << 1, 1), NTT(B, len << 1, 1);
        Rep(i, len << 1) A[i] = (LL)A[i] * B[i] % MOD;
        NTT(A, len << 1, -1);
        For(i, mid + 1, r) inc(f[i], A[i - l]);
        cdqFFT(mid + 1, r);
    }
    void Main() {
        if (y == 1) {
            printf("%d\n", fpm(n, 2 * n - 4));
            return;
        }

        InitFac(n);
        z_ = z - 1, iz_ = fpm(z_);

        cdqFFT(0, N - 1);

        printf("%lld\n", (LL)f[n] * fpm(y, n) % MOD);
    }
}

原文地址:https://www.cnblogs.com/Hany01/p/10357729.html

时间: 2024-08-01 17:10:06

[WC2019] 数树的相关文章

WC2019 T1 数树

WC2019 T1 数树 传送门(https://loj.ac/problem/2983) Question 0 对于给定的两棵树,设记两颗树 \(A,B\) 的重边为 \(R(A,B)\),那么 \[ Ans=y^{n-R(A,B)} \] Question 1 给定其中一棵树,求第二棵树的所有情况下答案的总和 不妨令 \(y=y^{-1}\) ,最终答案就是 \(y^{-n}y^{R(A,B)}\). 在给定 \(A\) 的情况下,只需要统计 \(\sum\limits_B y^{R(A,B

Thair数 树状数组

题目: Erwin最近对一种叫"thair"的东西巨感兴趣... 在含有n个整数的序列a1,a2......an中, 三个数被称作"thair"当且仅当i<j<k且ai<aj<ak 求一个序列中"thair"的个数. Input (thair.in) 开始一个正整数n, 以后n个数a1~an. Output (thair.out) "thair"的个数 Sample Input Output 4 2 1

1006数树11

小黄山上死掉的树木,上次已经被全部更换上了新的树苗,经过半年左右已经开始茁壮成长.2015级的小伙伴想对小黄山上的数目进行清点,在一个矩形区域内进行数树.横着数有m棵,竖着数有n棵,问总共有多少棵? 输入有多组测试用例.对于每组测试用例,包含两个正整数,分别为m和n(0 < m, n <= 1000). 对于每组测试用例输出一个正整数,矩形区域内总共有多少棵树. 2 6 3 5 12 15 #include"stdio.h"#include"math.h"

【WC2019】数树 树形DP 多项式exp

题目大意 有两棵 \(n\) 个点的树 \(T_1\) 和 \(T_2\). 你要给每个点一个权值吗,要求每个点的权值为 \([1,y]\) 内的整数. 对于一条同时出现在两棵树上的边,这条边的两个端点的值相同. 若 \(op=0\),则给你两棵树 \(T_1,T_2\),求方案数. 若 \(op=1\),则给你一棵树 \(T_1\),求对于所有 \(n^{n-2}\) 种 \(T_2\),方案数之和. 若 \(op=2\),则求对于所有的 \(T_1,T_2\),求方案数之和. \(n\leq

【loj2983】【WC2019】数树

题目 两颗\(n\)个点的树T1和T2,有\(y\)种颜色; 现在给每个点染色,要求公共边端点的颜色相同,求: ? 1.op=0 , T1和T2都确定,求合法染色方案数: ? 2.op=1 , T1确定,求所有T2的合法染色方案数的和 ; ? 3.op=2 , 求所有(T1,T2)合法染色方案数的和: \(mod \ 998244353\)的值: $1 \le n \le 10^5 ?, ?1 \le y \lt 998244353 ?, ?op = { 0,1,2 } $ ; 题解 op=0

hdu 4911 求逆序对数+树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能减少一个逆序对,所以问题转换成如何求逆序对数. 归并排序或者树状数组都可搞 树状数组: 先按大小排序后分别标号,然后就变成了求1~n的序列的逆序数,每个分别查询出比他小的用i减,在把他的值插入即可 #include <cstdio> #include <cstdlib> #includ

求序列中满足Ai &lt; Aj &gt; Ak and i &lt; j &lt; k的组数 树状数组 HIT 2275 Number sequence

http://acm.hit.edu.cn/hoj/problem/view?id=2275 Number sequence   Source : SCU Programming Contest 2006 Final   Time limit : 1 sec   Memory limit : 64 M Submitted : 1632, Accepted : 440 Given a number sequence which has N element(s), please calculate

Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字

[jzoj 3175] 数树数 解题报告 (树链剖分)

interlinkage: https://jzoj.net/senior/#main/show/3175 description: 给定一棵N 个节点的树,标号从1~N.每个点有一个权值.要求维护两种操作:1. C i x(0<=x<2^31) 表示将i 点权值变为x2. Q i j x(0<=x<2^31) 表示询问i 到j 的路径上有多少个值为x的节点 solution: 链剖 把颜色离散化,对每种颜色分别搞一颗线段树 直接搞会炸空间,因此要动态开点 感觉树上莫队好像也可以