返回顶部
动态线条
动态线条end

题解:CF2101F Shoo Shatters the Sunshine

这道题是可以纯组合数学直接做的,只是分讨和容斥比较繁琐。

假设颜色给定,考虑蓝点构成的虚树,其直径的中点 \(k\),以 \(k\) 为根,将红点加入虚树,其余未加入的点为白点,答案为红点的最大深度加直径长度的一半,即红蓝点最大深度之和。

那么我们枚举中点,中点不一定为蓝点,随后枚举深度 \(d\),在深度为 \(d\) 的那一层点中会有两个不同分支的点被染成蓝色,深度大于 \(d\) 的点不能染蓝色,以内的点颜色任意。然后再枚举红点的最大深度 \(h\),深度大于 \(h\) 的点不能染红色,以内的点颜色任意,深度为 \(d,h\) 的点任意染色会有重复,容斥一下。

对于长度为奇数的直径,每条边也可做为中点,在每条边建一个虚点连接原边接的两点,随后按正常方式计算即可。

每层染色方案独立,答案为每层染色方案的乘积,考虑每层的方案数如何计算,分 \(9\) 类讨论:

深度范围 允许颜色 方案数
\(i < \min(d,h)\) 红蓝白 \(3^{s_i}\)
\(d<h\) 时的 \(i=d\) 红蓝白,蓝需来自不同分支 \(A_i\)
\(d<h\) 时的 \(d<i<h\) 红白 \(2^{s_i}\)
\(d<h\) 时的 \(i=h\) 红白,红至少一个 \(2^{s_i}-1\)
\(d>h\) 时的 \(i=h\) 红蓝白,红至少一个 \(3^{s_i}-2^{s_i}\)
\(d>h\) 时的 \(h<i<d\) 蓝白 \(2^{s_i}\)
\(d>h\) 时的 \(i=d\) 蓝白,蓝需来自不同分支 \(B_i\)
\(d=h\) 时的 \(i=d\) 红蓝白,蓝需来自不同分支且红至少一个 \(A_i - B_i\)
\(i > \max(d,h)\) \(1\)

其中 \(s_i\) 表示第 \(i\) 层的节点数,\(c_{i,j}\) 表示第 \(j\) 条分支在第 \(i\) 层的节点数,接下来看 \(A_i\)\(B_i\) 如何计算。

\(A_i\)

总数 \(3^{s_i}\) 减去不满足条件的情况:

  • 蓝点 \(0\) 个:\(2^{s_i}\)
  • 蓝点恰 \(1\) 个:\(s_i\times 2^{s_i-1}\)
  • 蓝点 \(\ge 2\) 但全在分支 \(j\) 内:\((3^{c_{i,j}}-2^{c_{i,j}}-c_{i,j}2^{c_{i,j}-1})\times 2^{s_i-c_{i,j}}\)

\[A_i = 3^{s_i} - 2^{s_i} - s_i\times 2^{s_i-1} - \sum_{j=1}^m \bigl(3^{c_{i,j}}-2^{c_{i,j}}-c_{i,j}2^{c_{i,j}-1}\bigr)2^{s_i-c_{i,j}} \]

\(B_i\)

总数 \(2^{s_i}\) 减去不满足条件的情况:

  • 蓝点 \(0\)\(1\)
  • 蓝点 \(1\)\(s_i\)
  • 全在同一分支 \(j\)\(\ge 2\)\(2^{c_{i,j}}-c_{i,j}-1\)

\[B_i = 2^{s_i}-s_i-1-\sum_{j=1}^m (2^{c_{i,j}}-c_{i,j}-1) = 2^{s_i}+m-1-\sum_{j=1}^m 2^{c_{i,j}} \]

当然根也可以染色,分 \(7\) 类讨论:

深度范围 允许颜色 方案数
实点,\(d=h=0\) 不合法 \(0\)
实点,\(d=0\) \(1\)
实点,\(h=0\) \(1\)
实点,\(d>0,h>0\) 红蓝白 \(3\)
虚点,\(d=0\) 不合法 \(0\)
虚点,\(h=0\) 不合法 \(0\)
虚点,\(d>0,h>0\) \(1\)

dfs 预处理每个分支在每层的节点数与每层的节点总数,然后计算每层方案数与 \(d+h\) 的乘积就是当前根的答案。

直接这么做是 \(O(n^4)\) 的,但很明显可以优化。

\(f(i,d,h)\) 表示第 \(i\) 层按分讨计算出的方案数,\(g(u)\) 表示根 \(u\) 的染色方案数,以 \(u\) 为根的最大深度为 \(D\),那么答案为:

\[\sum_u\sum_{d=0}^D\sum_{h=0}^D(d+h)g(u)\sum_{i=1}^Df(i,d,h) \]

注意到每层的方案数的计算方式按类别可以分为 \(5\) 个部分,分别是:\(i<\min(d,h)\)\(i=d\)\(i=h\)\(i\)\(d,h\) 之间,\(i=d\)\(i=h\)\(i>\max(d,h)\)。也就是 \(d,h\) 的位置把它们划分成了三个区间,其中后缀积恒为 \(1\),不用考虑,前缀是 \(3^{s_i}\) 的乘积,中间的区间是 \(2^{s_i}\) 的乘积,可以维护前缀积和乘法逆元加速计算。

全部预处理后就不用一个一个枚举每一层了,时间复杂度优化至 \(O(n^3)\),在随机树上快到飞起,但 CF 的数据是不会轻易让你通过的,当然继续优化也不难。

考虑再优化掉一层循环,分别处理 \(d<h,d>h,d=h\) 的情况。

我们预处理:

\[pre_{2,n}=\prod_{i=1}^n2^{s_i}\\pre_{3,n}=\prod_{i=1}^n3^{s_i}\\prv_n=\prod_{i=1}^n(2^{s_i})^{-1}\\pred_n=\sum_{i=1}^npre_{2,i-1}\times B_i\\preh_n=\sum_{i=1}^npre_{2,i-1}\times (2^{s_i}-1)\\F_n=\sum_{i=1}^npre_{2,i-1}\times B_i\times i\\G_n=\sum_{i=1}^npre_{2,i-1}\times (2^{s_i}-1)\times i \]

注意以上运算均在模意义下进行。

\(d<h\)

枚举 \(d\)\(h\) 的范围为 \(d<h\le D\),共 \(D-d\) 种配对,方案数为:

\[\begin{cases} g(u)\times preh_D & d=0 \\ g(u)\times pre_{3,d-1}\times A_d\times(preh_D-preh_d)\times prv_d & d>0 \end{cases}\]

贡献为:

\[\begin{cases} G_D\times g(u) & d=0 \\ (G_D-G_d+d\times(preh_D-preh_d))\times g(u)\times pre_{3,d-1}\times A_d\times prv_d & d>0 \end{cases}\]

\(u\) 为实点,\(d=0\)\(g(u)=1\)\(d>0\)\(g(u)=3\)
\(u\) 为虚点,\(d=0\)\(g(u)=0\)\(d>0\)\(g(u)=1\)

\(d>h\)

和上面的相同,枚举 \(h\)\(d\) 的范围为 \(h<d\le D\),共 \(D-h\) 种配对,方案数为:

\[\begin{cases} g(u)\times pred_D & h=0 \\ g(u)\times pre_{3,h-1}\times (3^{s_h}-2^{s_h})\times(pred_D-pred_h)\times prv_h & h>0 \end{cases}\]

贡献为:

\[\begin{cases} F_D\times g(u) & h=0 \\ (F_D-F_h+h\times(pred_D-pred_h))\times g(u)\times pre_{3,h-1}\times (3^{s_h}-2^{s_h})\times prv_h & h>0 \end{cases}\]

\(u\) 为实点,\(h=0\)\(g(u)=1\)\(h>0\)\(g(u)=3\)
\(u\) 为虚点,\(h=0\)\(g(u)=0\)\(h>0\)\(g(u)=1\)

\(d=h\)

这个可以直接枚举,方案数为:

\[\begin{cases} 0 & i=0 \\ g(u)\times pre_{3,i-1}\times(A_i-B_i) & i>0 \end{cases}\]

贡献为:

\[\begin{cases} 0 & i=0 \\ 2i\times g(u)\times pre_{3,i-1}\times(A_i-B_i) & i>0 \end{cases}\]

\(u\) 为实点,\(i=0\)\(g(u)=0\)\(i>0\)\(g(u)=3\)
\(u\) 为虚点,\(i=0\)\(g(u)=0\)\(i>0\)\(g(u)=1\)

注意由于我们添加了虚节点,\(d,h\) 的大小翻了一倍,实际答案为原来的一半,乘上一个 \(2\) 的逆元即可。

最后分析一下时间复杂度,设 \(m_i\) 表示分支数即 \(i\) 的度数,\(D_i\) 表示以 \(i\) 为根的最大深度,那么单根预处理的复杂度为 \(O(D_i\times m_i)\),计算贡献的复杂度为 \(O(D_i)\),总复杂度为 \(O(\sum D_i\times m_i)\)\(D_i\) 的量级是 \(O(n)\) 的,看作 \(n\) 提出去:\(O(n\sum m_i)\),由于树的总度数是 \(O(n)\) 级别的,因此时间复杂度均摊下来其实是 \(O(n^2)\),可以通过。

参考实现

#include<bits/stdc++.h>
#define cin_fast ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define fi first
#define se second
//#define int long long 
#define in(a) a = read()
#define rep(i , a , b) for(int i = a ; i <= b ; i ++)
using namespace std;
typedef long long ll;
const int N = 6e3 + 5 , mod = 998244353;
const int inf = 0x3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f , inv_2 = 499122177; 
inline int read() {
	int x = 0;
	char ch = getchar();
	bool f = 0;
	while('9' < ch || ch < '0') f |= ch == '-' , ch = getchar();
	while('0' <= ch && ch <= '9') x = (x << 3) + (x << 1) + ch - '0' , ch = getchar();
	return f ? -x : x;
}
struct Edge{
    int n , v;
}edge[N << 1];
int head[N] , eid , dag[N];
void eadd(int u , int v) {
    edge[++ eid].n = head[u] , edge[head[u] = eid].v = v;
    edge[++ eid].n = head[v] , edge[head[v] = eid].v = u;
    dag[u] ++ , dag[v] ++;
}
ll pw2[N] , pw3[N] , inv2[N] , pre2[N] , pre3[N] , prv[N] , pred[N] , preh[N] , F[N] , G[N];
int c[N][N] , s[N] , vt , m , D , n;
void gd(int u , int fa , int dep) {
    if(u <= n) D = max(D , dep);
    for(int i = head[u] ; i ; i = edge[i].n) {
        int v = edge[i].v;
        if(v == fa) continue;
        gd(v , u , dep + 1);
    }
}
void dfs(int u , int fa , int dep , int id) {
    if(u <= n) c[dep][id] ++ , s[dep] ++;
    for(int i = head[u] ; i ; i = edge[i].n) {
        int v = edge[i].v;
        if(v == fa) continue;
        dfs(v , u , dep + 1 , id);
    }
}
ll add(ll x , ll y) {
    x += y;
    if(x >= mod) x-= mod;
    return x;
}
ll sub(ll x , ll y) {
    x -= y;
    if(x < 0) x += mod;
    return x;
}
ll mul(ll x , ll y) {
    return (x * y) % mod;
}
ll A(int i) {
    if(s[i] == 0) return 0;
    ll ans = sub(sub(pw3[s[i]] , pw2[s[i]]) , mul(s[i] , pw2[s[i] - 1])) , now = 0;
    for(int j = 1 ; j <= m ; j ++) {
        if(c[i][j] == 0) continue; 
        now = add(now , mul(sub(sub(pw3[c[i][j]] , pw2[c[i][j]]) , mul(c[i][j] , pw2[c[i][j] - 1])) , pw2[s[i] - c[i][j]]));
    }
    return sub(ans , now);
}
ll B(int i) {
    if(s[i] == 0) return 0;
    ll ans = add(pw2[s[i]] , m - 1) , now = 0;
    for(int j = 1 ; j <= m ; j ++) now = add(now , pw2[c[i][j]]);
    return sub(ans , now);
}
ll g(int u , int dep) {
    if(u <= n) return dep == 0 ? 1 : 3;
    else return dep == 0 ? 0 : 1;
}
ll a[N] , b[N];
signed main() {
	//cin_fast;
    pre2[0] = pre3[0] = prv[0] = pw2[0] = pw3[0] = inv2[0] = 1;
    for(int i = 1 ; i < N ; i ++) {
        pw2[i] = mul(pw2[i - 1] , 2);
        pw3[i] = mul(pw3[i - 1] , 3);
        inv2[i] = mul(inv2[i - 1] , inv_2);
    }
    int T;
    in(T);
    while(T --) {
        eid = 0;
        memset(head , 0 , sizeof(head)) , memset(dag , 0 , sizeof(dag));
        ll ans = 0;
        in(n);
        int tot = n;
        for(int i = 1 ; i < n ; i ++) {
            int u , v;
            in(u) , in(v);
            eadd(u , ++ tot) , eadd(tot , v);
        }  
        for(int u = 1 ; u <= tot ; u ++) {
            m = dag[u] , D = 0;
            gd(u , 0 , 0);
            for(int i = 0 ; i <= D ; i ++) {
                memset(c[i] , 0 , (m + 1) * sizeof(int));
                s[i] = 0;
            }
            for(int i = head[u] , j = 1 ; i ; i = edge[i].n , j ++) {
                int v = edge[i].v;
                dfs(v , u , 1 , j);
            }
            for(int i = 1 ; i <= D ; i ++) {
                a[i] = A(i) , b[i] = B(i);
                pre2[i] = mul(pre2[i - 1] , pw2[s[i]]);
                pre3[i] = mul(pre3[i - 1] , pw3[s[i]]);
                prv[i] = mul(prv[i - 1] , inv2[s[i]]);
                pred[i] = add(pred[i - 1] , mul(pre2[i - 1] , b[i]));
                preh[i] = add(preh[i - 1] , mul(pre2[i - 1] , sub(pw2[s[i]] , 1)));
                F[i] = add(F[i - 1] , mul(i , mul(pre2[i - 1] , b[i])));
                G[i] = add(G[i - 1] , mul(i , mul(pre2[i - 1] , sub(pw2[s[i]] , 1))));
            }
            ans = add(ans , mul(g(u , 0) , G[D]));
            for(int d = 1 ; d <= D ; d ++) {
                ll now = add(sub(G[D] , G[d]) , mul(d , sub(preh[D] , preh[d])));
                now = mul(now , mul(g(u , d) , mul(pre3[d - 1] , mul(a[d] , prv[d]))));
                ans = add(ans , now);
            }
            ans = add(ans , mul(g(u , 0) , F[D]));
            for(int h = 1 ; h <= D ; h ++) {
                ll now = add(sub(F[D] , F[h]) , mul(h , sub(pred[D] , pred[h])));
                now = mul(now , mul(g(u , h) , mul(pre3[h - 1] , mul(sub(pw3[s[h]] , pw2[s[h]]) , prv[h]))));
                ans = add(ans , now);
            }
            for(int i = 1 ; i <= D ; i ++) {
                ll now = 2 * i;
                now = mul(now , mul(g(u , i) , mul(pre3[i - 1] , sub(a[i] , b[i]))));
                ans = add(ans , now);
            }
        }
        cout << mul(ans , inv_2) << '\n';
    }
	return 0;
}
posted @ 2026-05-25 11:21  Kei酱  阅读(12)  评论(0)    收藏  举报