[题解] [AGC005F] Many Easy Problems

题面

题解

学长讲课题目的质量果然和我平常找的那些不一样

思路还是可以说比较巧妙的

考虑到我们并不好算出对于所有大小为 \(i\) 的点集,能够包含它的最小连通块大小

转换题目

这个时候我们应该想到把目标放到单个点 \(i\) 对选择 \(k\) 个点时的贡献

那么他的贡献就是总方案数减去没选的方案数对吧

没选的方案怎么算呢

记 \(sz[i]\) 为以 \(i\) 为根的子树的点数

分析发现, 当 \(k\) 个点同时在以它的儿子为根的一棵子树内就不会计算 \(i\)

这里的儿子的意义是, 以 \(i\) 为全树的根时它的儿子

也就是说, 以它的父亲为根, 大小为 \(n - sz[i]\) 的子树也算作 \(i\) 的子树

然后大力推式子
\[
\displaystyle\begin{aligned}f(k) = \sum_{u=1}^n(C_n^k-\sum_{v\in sonu}C_{sz[v]}^k)\end{aligned}
\]
\(C_n^k\) 是总方案数, 后面那个就是不会计算 \(i\) 的方案数

但是我们又发现, 这个东西不是很好算

于是又一次转换, 我们设 \(cnt_i\) 为大小为 \(i\) 的子树的个数

然后把相同大小的子树的贡献归集起来有
\[
\displaystyle\begin{aligned}f(k)&=n*C_n^k - \sum_{i=k}^ncnt_i*C_i^k\\&=n*C_n^k-\sum_{i=k}^{n}cnt_i*\frac{i!}{k!*(i-k)!}\\&=n*C_n^k-\frac{1}{k!}\sum_{i=k}^{n}\frac{cnt_i*i!}{(i-k)!}\end{aligned}
\]
令 \(F_i = cnt_i*i!\) , \(G_i = \frac{1}{i!}\) , \(H_i = G_{n-i}\)

所以有
\[
\displaystyle\begin{aligned}f(k)&=n*C_n^k-\frac{1}{k!}\sum_{i=k}^{n}F_i*H_{n+k-i}\end{aligned}
\]
NTT 即可

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int mod = 924844033;
const int N = 200005;
using namespace std;

int m, lim, n, g, ig, p[105], cnt, f[N * 8], inv[2 * N], fac[2 * N], h[N * 8], sz[N], head[N], rev[N * 8];
struct edge { int to, nxt; } e[N << 1]; 

template < typename T >
inline T read()
{
    T x = 0, w = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * w;
}

inline void adde(int u, int v) { e[++cnt] = (edge) { v, head[u] }, head[u] = cnt; }

int fpow(int x, int y)
{
    int res = 1;
    for( ; y; y >>= 1, x = 1ll * x * x % mod)
        if(y & 1) res = 1ll * res * x % mod;
    return res;
}

int getroot(int x)
{
    int rem = x - 1, tmp = x - 1;
    for(int i = 2; i * i <= rem; i++)
        if(!(tmp % i))
        {
            p[++cnt] = i;
            while(!(tmp % i)) tmp /= i;
        }
    if(tmp > 1) p[++cnt] = tmp;
    for(int flag = 0, i = 2; i <= x; i++, flag = 0)
    {
        for(int j = 1; j <= cnt; j++)
            if(fpow(i, rem / p[j]) == 1) flag = 1;
        if(!flag) return i;
    }
}

void pre()
{
    for(int i = (fac[0] = 1); i <= 2 * n; i++)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[2 * n] = fpow(fac[2 * n], mod - 2);
    for(int i = 2 * n - 1; i >= 0; i--)
        inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
    for(int i = 1; i <= n; i++)
        h[i] = inv[n - i];
}

int C(int n, int m) { return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; }

void dfs(int u, int fa)
{
    sz[u] = 1;
    for(int v, i = head[u]; i; i = e[i].nxt)
    {
        v = e[i].to; if(v == fa) continue;
        dfs(v, u), sz[u] += sz[v];
    }
    f[sz[u]]++, f[n - sz[u]]++;
}

void ntt(int *p, int opt)
{
    for(int i = 0; i < n; i++)
        if(i < rev[i]) swap(p[i], p[rev[i]]);
    for(int i = 1; i < n; i <<= 1)
    {
        int rt = fpow(opt == 1 ? g : ig, (mod - 1) / (i << 1));
        for(int j = 0; j < n; j += (i << 1))
        {
            int w = 1;
            for(int k = j; k < j + i; k++, w = 1ll * w * rt % mod)
            {
                int x = p[k], y = 1ll * w * p[k + i] % mod;
                p[k] = (x + y) % mod, p[k + i] = (x - y + mod) % mod;
            }
        }
    }
}

int main()
{
    n = read <int> (), g = getroot(mod), ig = fpow(g, mod - 2);
    cnt = 0, pre();
    for(int u, v, i = 1; i < n; i++)
    {
        u = read <int> (), v = read <int> ();
        adde(u, v), adde(v, u);
    }
    dfs(1, 0);
    for(m = 3 * n, n = 1; n <= m; n <<= 1, lim++); lim--;
    for(int i = 0; i < n; i++)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lim);
    f[m / 3]--;
    for(int i = 0; i < m / 3; i++)
        f[i] = 1ll * f[i] * fac[i] % mod;
    ntt(f, 1), ntt(h, 1);
    for(int i = 0; i < n; i++)
        f[i] = 1ll * f[i] * h[i] % mod;
    ntt(f, -1);
    int tmp = fpow(n, mod - 2);
    for(int i = 0; i < n; i++)
        f[i] = 1ll * f[i] * tmp % mod;
    m /= 3;
    for(int i = 1; i <= m; i++)
        printf("%lld\n", (1ll * m * C(m, i) % mod - 1ll * inv[i] * f[m + i] % mod + mod) % mod);
    return 0;
}

原文地址:https://www.cnblogs.com/ztlztl/p/12194874.html

时间: 2024-10-09 23:07:11

[题解] [AGC005F] Many Easy Problems的相关文章

AGC005F - Many Easy Problems

题目描述 给定一棵树,设 $f_k$ 表示任选 $k$ 个点组成的最小连通块之和,求 $f(i),i \in [1,n]$ 数据范围 $n \le 2 \times 10^5$ 题解 考虑容斥,即一个点对于 $k$ 不会产生贡献的方案数 于是对于一个点 $u$ ,设其为根,它对 $k$ 的贡献可以列出式子: $(_k^n)-\sum_{v \in son_u}(_k^{size_v})$ 于是 $f_k=n(_k^n)-\sum_{i=1}^ng_i(_k^{i})$ ,其中 $g_i$ 表示子

【题解】AT2064 Many Easy Problems(转换+NTT)

[题解]AT2064 Many Easy Problems(转换+NTT) 给定一棵树,请你回答\(k\in[1,n]\)由\(k\)个点生成出来的虚树(steiner)的所有方案的大小的和. 对于这种分元素然后每个元素对答案有一个相同的贡献的计数,一般都是考虑对于一个点考虑对于答案的贡献.对于一个确定的\(k\)和一个点\(p\),可以很轻易的算出\(p\)对于答案的贡献=\({n\choose k }-( \sum_{u \in Son}siz[u])-(n-siz[p])\).我们拿个同记

【AtCoder】AGC005 F - Many Easy Problems 排列组合+NTT

[题目]F - Many Easy Problems [题意]给定n个点的树,定义S为大小为k的点集,则f(S)为最小的包含点集S的连通块大小,求k=1~n时的所有点集f(S)的和取模924844033.n<=2*10^5. [算法]排列组合+NTT [题解]考虑每个点只会在k个点都在其一个子树时无贡献,即: $$ANS_k=\sum_{x=1}^{n}\binom{n}{k}-\sum_{y}\binom{sz[y]}{k}+\binom{n-sz[y]}{k}$$ 令$cnt_i$表示满足s

Codeforces B. Too Easy Problems

题目描述: time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are preparing for an exam on scheduling theory. The exam will last for exactly T milliseconds and will consist of n problems. You

【题解】An Easy Problem

题目描述 给定一个正整数N,求最小的.比N大的正整数M,使得M与N的二进制表示中有相同数目的1. 举个例子,假如给定的N为78,其二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案. 输入格式 输入若干行,每行一个数n(1≤n≤1000000),输入"0"结束. 输出格式 输出若干行对应的值. 输入样例 1 2 3 4 78 0 输出样例 2 4 5 8 83 题解 容易想到,当$n$加上$low

《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就是一本题解书,简单暴力的通过题目的堆叠来提升解决编程问题的能力. 那么下面开始探索吧. zoj1037: BackgroundFor years, computer scientists have been trying to find efficient solutions to different computing p

[AGC005]:F - Many Easy Problem

F - Many Easy Problems Time limit : 5sec / Memory limit : 512MB Score : 1900 points Problem Statement One day, Takahashi was given the following problem from Aoki: You are given a tree with N vertices and an integer K. The vertices are numbered 1 thr

AGC005做题小记

比赛链接:AGC005 C. Tree Restoring 题意 给出树上每个节点到其它节点的最远距离,问是否可能. $ n ≤ 100 $ . 题解 离一个点最远的节点是直径两端点之一. 先把直径上的点切出来.剩余的点判断一下是不是在 $ \lceil \frac{d}{2} \rceil +1 $ 和 $ d + 1 $ 之间即可. My Submission D. ~K Perm Counting 题意 计数 $ n $ 个数全排列个数,使得 $ abs(a_i - i) ≠ K $ $

重新写的Gridland

这道题是我很久以前的时候写的,今天我同学问我这道题怎么做,本想偷懒来着,直接看看,但是,没有任何的线索告诉我从那个角度出发,哎,这也是我不足的地方,下面先给题目,分析在后面. Background For years, computer scientists have been trying to find efficient solutions to different computing problems. For some of them efficient algorithms are