「JSOI2019」神经网络(容斥+组合计数+背包dp)

Address

luogu5333
loj3102

Solution

容易发现,一条哈密顿回路本质上就是:把每棵树都拆成若干条有向路径,再把所有的有向路径连接成环,环上的相邻两条有向路径不可以来自同一棵树。

先求出 \(g_{i,j}\) 表示把第 \(i\) 棵树拆成 \(j\) 条有向路径的方案数。

考虑 \(\text{dp}\),记 \(f_{u,i,0/1/2/3}\) 分别表示:\(u\) 的子树拆成 \(i\) 条路径,\(u\) 是路径起点,是路径终点,单点成路径,既不是路径起点也不是路径终点的方案数。

注意 \(f_{u,i,0/1}\) 不允许 \(u\) 单点成路径。转移随便讨论一下即可。最终 \(g_{i,j}=f_{u,i,0}+f_{u,i,1}+f_{u,i,2}+f_{u,i,3}\)。

接下来,假设我们对于所有的 \(i∈[1,m]\),已经确定第 \(i\) 棵树拆成 \(a_i\) 条链】路径,那么如何计算答案呢?

考虑容斥。枚举第 \(i\) 棵树的路径在环上被划分为至多 \(b_i\) 段。我们钦定第 \(m\) 棵树的 \(1\) 号节点所在的路径为环上第一条路径,那么在此条件下的方案数为:
\[(\prod_{i=1}^mC_{a_i-1}^{b_i-1})×(\prod_{i=1}^{m-1}g_{i,a_i}×a_i!)×g_{m,a_m}×(a_m-1)!×\frac{(\sum_{i=1}^{m-1}b_i)!}{\prod_{i=1}^{m-1}b_i!}×C_{(\sum_{i=1}^nb_i)-2}^{b_m-1}\]其中 \(\prod_{i=1}^mC_{a_i-1}^{b_i-1}\) 表示把每棵树的路径划分成 \(i\) 段的方案数,\(g_{i,a_i}×a_i!\) 表示在第 \(i\) 棵树上选出 \(a_i\) 条路径形成一个排列的方案数。

如果确定了选出的路径形成的排列是哪些,划分成的 \(b_i\) 段分别是什么,问题就转化为:第 \(i\) 种颜色的球有 \(b_i\) 个,同种颜色的球之间没有区别,求把所有的求串成环,使得相邻两个异色的方案数。

钦定环上第一条路径相当于钦定第一个球的颜色一定为 \(m\),最后一个球的颜色不是 \(m\),然后断环为链。所以先把前 \(m-1\) 种颜色的球排好(方案数为),最后在中间的 \((\sum b_i)-2\) 个位置中选出 \(b_m-1\) 个位置放颜色为 \(m\) 的球,剩下的空位给其它的球即可。

加上容斥系数之后对答案的贡献即\[(\prod_{i=1}^mC_{a_i-1}^{b_i-1})×(-1)^{\sum_{i=1}^ma_i-b_i}×(\prod_{i=1}^{m-1}g_{i,a_i}×a_i!)×g_{m,a_m}×(a_m-1)!×\frac{(\sum_{i=1}^{m-1}b_i)!}{\prod_{i=1}^{m-1}b_i!}×C_{(\sum_{i=1}^nb_i)-2}^{b_m-1}\]

可是直接枚举所有 \(a_i,b_i\) 的复杂度是指数级的,考虑优化。

记第 \(i\) 棵树的点数为 \(cnt_i\)。对于第 \(i\) 棵树(\(i∈[1,m-1]\)),枚举 \(j=a_i,k=b_i\),可以写出这样的一个多项式:

\[\sum_{j=1}^{cnt_i}g_{i,j}×i!\sum_{k=1}^jC_{j-1}^{k-1}×(-1)^{j-k}×\frac{x^k}{k!}\]

然后我们先把这 \(m-1\) 个多项式相乘,再把 \(x^k\) 的系数乘上 \(k!\)。这样得到的 \(x^k\) 的系数 \(A_k\) 相当于:对于 \(i∈[1,m-1]\),枚举所有 \(a_i,b_i\) 满足 \(\sum_{i=1}^{m-1}b_i=k\),然后把 \[(\prod_{i=1}^mC_{a_i-1}^{b_i-1})×(-1)^{\sum_{i=1}^ma_i-b_i}×(\prod_{i=1}^{m-1}g_{i,a_i}×a_i!)×\frac{(\sum_{i=1}^{m-1}b_i)!}{\prod_{i=1}^{m-1}b_i!}\] 计入 \(A_k\)。

接下来写出第 \(m\) 个多项式,记 \(B_k\) 表示这个多项式第 \(k\) 项的系数:

\[\sum_{j=1}^{cnt_m}g_{m,j}×(j-1)!×\sum_{k=1}^jC_{j-1}^{k-1}×(-1)^{j-k}×x^k\]

最后枚举 \(k=\sum_{i=1}^{m-1}b_i\),再枚举 \(j=b_m\),把 \(A_k×B_j×C_{k+j-2}^{j-1}\) 计入答案即可。

时间复杂度 \(O((\sum cnt_i)^2)\)。

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
    char ch;
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
        res = res * 10 + (ch ^ 48);
}

const int e = 5005, o = 305, mod = 998244353;

int g[o][e], f[e][e][4], sze[e], m, n, ans, tmp[e][4], cnt, pre[e], h[e], p[o][e];
int adj[e], nxt[e << 1], go[e << 1], num, tot[o], fac[e], inv[e], a[o][e];

inline void add(int &x, int y)
{
    (x += y) >= mod && (x -= mod);
}

inline int plu(int x, int y)
{
    add(x, y);
    return x;
}

inline int mul(int x, int y)
{
    return (ll)x * y % mod;
}

inline int ksm(int x, int y)
{
    int res = 1;
    while (y)
    {
        if (y & 1) res = mul(res, x);
        y >>= 1;
        x = mul(x, x);
    }
    return res;
}

inline void link(int x, int y)
{
    nxt[++num] = adj[x]; adj[x] = num; go[num] = y;
    nxt[++num] = adj[y]; adj[y] = num; go[num] = x;
}

inline int c(int x, int y)
{
    if (x < y) return 0;
    if (x == y) return 1;
    return mul(fac[x], mul(inv[y], inv[x - y]));
}

inline void clear()
{
    int i, j, k; num = ans = 0;
    for (i = 1; i <= n; i++)
    {
        adj[i] = 0;
        for (j = 1; j <= n; j++)
            for (k = 0; k <= 3; k++)
                f[i][j][k] = 0;
    }
}

inline void dfs(int u, int pa)
{
    sze[u] = f[u][1][2] = 1;
    int i, j, k, l;
    for (i = adj[u]; i; i = nxt[i])
    {
        int v = go[i];
        if (v == pa) continue;
        dfs(v, u);
        for (j = 1; j <= sze[u] + sze[v]; j++)
            for (k = 0; k <= 3; k++)
                tmp[j][k] = f[u][j][k], f[u][j][k] = 0;
        for (j = 1; j <= sze[u]; j++)
            for (k = 1; k <= sze[v]; k++)
            {
                int s = plu(f[v][k][0], f[v][k][2]);
                add(f[u][j + k - 1][0], mul(tmp[j][2], s));
                add(f[u][j + k - 1][3], mul(tmp[j][1], s));

                s = plu(f[v][k][1], f[v][k][2]);
                add(f[u][j + k - 1][1], mul(tmp[j][2], s));
                add(f[u][j + k - 1][3], mul(tmp[j][0], s));

                s = 0;
                for (l = 0; l <= 3; l++)
                    add(s, f[v][k][l]);
                for (l = 0; l <= 3; l++)
                    add(f[u][j + k][l], mul(tmp[j][l], s));
            }
        sze[u] += sze[v];
    }
}

inline void solve(int k)
{
    read(n); clear();
    cnt += n; tot[k] = n; pre[k] = tot[k] + pre[k - 1];
    int i, x, y, j;
    for (i = 1; i < n; i++)
        read(x), read(y), link(x, y);
    dfs(1, 0);
    for (i = 1; i <= n; i++)
        for (j = 0; j <= 3; j++)
            add(g[k][i], f[1][i][j]);
}

int main()
{
    read(m);
    int i, j, k;
    for (i = 1; i <= m; i++)
        solve(i);

    fac[0] = 1;
    for (i = 1; i <= cnt; i++)
        fac[i] = mul(fac[i - 1], i);
    inv[cnt] = ksm(fac[cnt], mod - 2);
    for (i = cnt - 1; i >= 0; i--)
        inv[i] = mul(inv[i + 1], i + 1);

    for (i = 1; i < m; i++)
        for (j = 1; j <= tot[i]; j++)
            g[i][j] = mul(g[i][j], fac[j]);

    for (j = 1; j <= tot[m]; j++)
        g[m][j] = mul(g[m][j], fac[j - 1]);

    for (i = 1; i < m; i++)
        for (j = 1; j <= tot[i]; j++)
            for (k = 1; k <= j; k++)
            {
                int v = mul(g[i][j], c(j - 1, k - 1));
                v = mul(v, inv[k]);
                if ((j - k) & 1) add(p[i][k], mod - v);
                else add(p[i][k], v);
            }

    for (j = 1; j <= tot[m]; j++)
        for (k = 1; k <= j; k++)
        {
            int v = mul(g[m][j], c(j - 1, k - 1));
            if ((j - k) & 1) add(p[m][k], mod - v);
            else add(p[m][k], v);
        }

    a[0][0] = 1;
    for (i = 1; i < m; i++)
        for (j = 1; j <= pre[i]; j++)
            for (k = 1; k <= j && k <= tot[i]; k++)
                add(a[i][j], mul(a[i - 1][j - k], p[i][k]));

    for (i = 1; i <= pre[m - 1]; i++)
        for (j = 1; j <= tot[m]; j++)
        {
            int x = mul(a[m - 1][i], fac[i]), y = p[m][j];
            add(ans, mul(x, mul(y, c(i + j - 2, j - 1))));
        }
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/cyf32768/p/12196010.html

时间: 2024-10-01 20:51:20

「JSOI2019」神经网络(容斥+组合计数+背包dp)的相关文章

bzoj4767两双手 容斥+组合

4767: 两双手 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 684  Solved: 208[Submit][Status][Discuss] Description 老W是个棋艺高超的棋手,他最喜欢的棋子是马,更具体地,他更加喜欢马所行走的方式.老W下棋时觉得无聊,便 决定加强马所行走的方式,更具体地,他有两双手,其中一双手能让马从(u,v)移动到(u+Ax,v+Ay)而另一双手能让 马从(u,v)移动到(u+Bx,v+By).小W看见老

4487[Jsoi2015]染色问题 容斥+组合

4487: [Jsoi2015]染色问题 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 211  Solved: 127[Submit][Status][Discuss] Description 棋盘是一个n×m的矩形,分成n行m列共n*m个小方格.现在萌萌和南南有C种不同颜色的颜料,他们希望把棋盘用这些颜料染色,并满足以下规定:1.  棋盘的每一个小方格既可以染色(染成C种颜色中的一种) ,也可以不染色.2.  棋盘的每一行至少有一个小方格被染

AtCoder AGC039F Min Product Sum (容斥原理、组合计数、DP)

题目链接 https://atcoder.jp/contests/agc039/tasks/agc039_f 题解 又是很简单的F题我不会... 考虑先给每行每列钦定一个最小值\(a_i,b_j\),并假设每行每列的最小值是这个数,且每行每列只需要放\(\ge\)这个数的数即可,那么这种情况的价值是\(\prod^n_{i=1}\prod^m_{j=1}\min(a_i,b_j)\), 方案数是\(\prod^n_{i=1}\prod^m_{j=1}(n+1-\max(a_i,b_j))\) 然

codeforces 277.5 div2 F:组合计数类dp

题目大意: 求一个 n*n的 (0,1)矩阵,每行每列都只有两个1 的方案数 且该矩阵的前m行已知 分析: 这个题跟牡丹江区域赛的D题有些类似,都是有关矩阵的行列的覆盖问题 牡丹江D是求概率,这个题是方案数,也比较相似.. 这种题中,因为只要求方案数..我们只要关注几行几列有几个1,而不必要关注具体的位置 题解: 行列都需要处理,因此考虑记录列的状态,然后一行一行的转移 最暴力的方程: dp[i][j][k][t] 表示已经确定了 i 行 有 j列已经有两个1,有k列只有一个1,有t列一个1也没

hdu5136:组合计数、dp

题目大意: 求直径长度为N的无根二叉树的个数(同构的只算一种) 分析: 分析发现直径长度不好处理!因此考虑把问题转化一下: 假设要求直径为N的二叉树 (1) 若N为偶数,将树从直径中点的边断开,则分成了两个深度为 n/2 的有根树 (为什么要这么分?因为若深度大于 n/2 那么子书的直径就有可能大于n了!) 用num[n/2]代表n/2的有根树的个数 那么答案则为  c(num[n/2],2)+num[n/2].. (注意判重,c(x,x)部分代表两个子树不一样的情况,后面单独的num[]代表两

Loj 6433. 「PKUSC2018」最大前缀和 (状压dp)

题面 Loj 题解 感觉挺难的啊- 状压\(dp\) 首先,有一个性质 对于一个序列的最大前缀和\(\sum_{i=1}^{p} A[i]\) 显然对于每个\(\sum_{i=p+1}^{x}A[i](p+1 \leq x \leq n)<0\) 我们可以以\(p\)分成两个集合 \(n\leq 20\) 所以状压一下 \(sum[i]\)表示当前状态表示的和 \(f[i]\)表示用当前状态的数,组成最大前缀和为\(sum[i]\)的方案数 \(g[i]\)表示当前状态的数,组成的序列,每个前缀

AtCoder AGC032F One Third (组合计数、DP、概率期望、微积分)

题目链接 https://atcoder.jp/contests/agc032/tasks/agc032_f 题解 神仙题.. 第一步转化利用了\(\frac{1}{3}\)这个数特有的性质.假设我们用红线标出每一次切割的位置,再在每一次切割的位置顺时针\(120\)度处用蓝线标出,那么答案就等于红线与蓝线之间的最小夹角.但是这样转化完了依然不好做(而且似乎也没用到\(\frac{1}{3}\)的特殊性),那么考虑如果在每一次切割的位置逆时针\(120\)度处用绿线标出,答案依然是不变的,因为\

HDU 4135 Co-prime(组合+容斥)

Problem Description Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N. Two integers are said to be co-prime or relatively prime if they have no common positive divisors other tha

[Matrix-Tree][插值][容斥][prufer序列][DP]夕张的改造

源自 krydom 大爷的 FJ 省冬令营模拟赛题 Statement 给定一棵 \(n\) 个点的树和一个参数 \(k\) 每次操作可以选出树上的一条边删掉,然后再加一条边,使得操作之后还是一棵树 求 \(k\) 次操作能得到多少种不同的树,对 \(998244353\) 取模 \(n\le 50\)(原题数据范围) \(n\le 2000\)(加强版) \(n\le 10^5\)(我不会,可以问 PinkRabbit) Solution 1:Matrix-Tree 矩阵树定理+拉格朗日插值