1 条题解

  • 1
    @ 2022-9-12 0:28:05

    这题的题意就是考虑一棵点分树的还原。考虑任意一棵子树接上它根结点的父亲uu。可以发现,这棵子树的任何一个结点都可以接上 uu,因为点分树完全改变了原树结构,uu 的儿子只是它对应子树的重心,与实际位置没有任何关系。而路径一共有两种情况:一端是uu的路径和两端都不是 uu,即原子树内部的路径。原子树内部的直接上传答案就好了,连接 uu 的贡献也比较好计算; 但要注意一条长度不小于 11 的路径有两种连接 uu 的方式。

    接下来考虑 uu 的所有子树合并的情况。子树合并时需要考虑跨子树的路径,即两个子树内连接uu的那些路径的结合。分别枚举长度,利用之前情况得到的信息,边dfs边依次计算贡献即可。

    同时需要注意的是,每条路径都要乘上与它无关的部分的方案数,这部分意味着除了它之外其他结点对原树可能的贡献。在合并为新路径前就需要计算好,因为它在所有的原树里都有贡献。这部分的贡献也可以边dfs边计算,想清楚各个dp的顺序即可。

    最后输出每一个对应长度的方案数即可。复杂度瓶颈在跨子树的方案计算,由于点分树每层结点数至少扩大2倍,考虑最长链长度,时间复杂度可能是O(n2)O(n^2)

    实现其实很短。提供一份jiangly的solution:

    #include <cstdio>
    #include<algorithm>
    #include<vector>
    #include<iostream>
    #include<cassert>
    constexpr int P = 998244353;
    using i64 = long long;
    // assume -P <= x < 2P
    int norm(int x) {
        if (x < 0) {
            x += P;
        }
        if (x >= P) {
            x -= P;
        }
        return x;
    }
    template<class T>
    T power(T a, int b) {
        T res = 1;
        for (; b; b /= 2, a *= a) {
            if (b % 2) {
                res *= a;
            }
        }
        return res;
    }
    struct Z {
        int x;
        Z(int x = 0) : x(norm(x)) {}
        int val() const {
            return x;
        }
        Z operator-() const {
            return Z(norm(P - x));
        }
        Z inv() const {
            assert(x != 0);
            return power(*this, P - 2);
        }
        Z &operator*=(const Z &rhs) {
            x = i64(x) * rhs.x % P;
            return *this;
        }
        Z &operator+=(const Z &rhs) {
            x = norm(x + rhs.x);
            return *this;
        }
        Z &operator-=(const Z &rhs) {
            x = norm(x - rhs.x);
            return *this;
        }
        Z &operator/=(const Z &rhs) {
            return *this *= rhs.inv();
        }
        friend Z operator*(const Z &lhs, const Z &rhs) {
            Z res = lhs;
            res *= rhs;
            return res;
        }
        friend Z operator+(const Z &lhs, const Z &rhs) {
            Z res = lhs;
            res += rhs;
            return res;
        }
        friend Z operator-(const Z &lhs, const Z &rhs) {
            Z res = lhs;
            res -= rhs;
            return res;
        }
        friend Z operator/(const Z &lhs, const Z &rhs) {
            Z res = lhs;
            res /= rhs;
            return res;
        }
    };
    
    int main() {
        
    	char INPUT[50],OUTPUT[50];
    /*	for(int TEST=0;TEST<=33;++TEST)
    	{
    		std::cerr<<TEST<<std::endl;
    		sprintf(INPUT,"MSF_Akatsuki%d.in",TEST);
    		sprintf(OUTPUT,"MSF_Akatsuki%d.out",TEST);
    		freopen(INPUT,"r",stdin);
    		freopen(OUTPUT,"w",stdout);*/
        int n, rt;
        std::cin >> n >> rt;
        rt--;
        
        std::vector<std::vector<int>> adj(n);
        for (int i = 0; i < n - 1; i++) {
            int u, v;
            std::cin >> u >> v;
            u--;
            v--;
            adj[u].push_back(v);
            adj[v].push_back(u);
        }
        
        std::vector<int> siz(n);
        std::vector f(n, std::vector<Z>(n));
        std::vector<Z> w(n);
        
        auto dfs = [&](auto self, int u, int p) -> void {
            siz[u] = 1;
            w[u] = 1;
            f[u][0] = 1;
            std::vector<Z> g(n);
            g[0] = 1;
            for (auto v : adj[u]) {
                if (v == p) {
                    continue;
                }
                self(self, v, u);
                
                std::vector<Z> nf(n), ng(n);
                for (int i = 0; i < siz[u]; i++) {
                    nf[i] += f[u][i] * w[v] * siz[v];
                    ng[i] += g[i] * w[v] * siz[v];
                }
                for (int i = 0; i < siz[v]; i++) {
                    nf[i] += f[v][i] * w[u] * siz[v];
                    ng[i + 1] += f[v][i] * w[u] * (1 + (i > 0));
                }
                for (int i = 0; i < siz[v]; i++) {
                    for (int j = 0; j < siz[u]; j++) {
                        nf[i + j + 1] += f[v][i] * (1 + (i > 0)) * g[j];
                    }
                }
                
                f[u] = nf;
                g = ng;
                
                w[u] *= w[v] * siz[v];
                siz[u] += siz[v];
            }
            
            for (auto v : adj[u]) {
                if (v == p) {
                    continue;
                }
                if (2 * siz[v] > siz[u]) {
                    for (int i = 1; i < n; i++) {
                        std::cout << 0 << " \n"[i == n - 1];
                    }
                    std::exit(0);
                }
            }
        };
        
        dfs(dfs, rt, -1);
        
        for (int i = 1; i < n; i++) {
            std::cout << f[rt][i].val() << " \n"[i == n - 1];
        }
    /*    fclose(stdin);
    	fclose(stdout);
    	}*/
        return 0;
    }
    

    信息

    ID
    157
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    (无)
    递交数
    78
    已通过
    2
    上传者