Loj #3102. 「JSOI2019」神经网络

题目背景

火星探险队发现,火星人的思维方式与人类非常不同,是因为他们拥有与人类很不一样的神经网络结构。为了更好地理解火星人的行为模式,JYY 对小镇上火星人的大脑进行了扫描,得到了一些重要数据。

题目描述

火星人在出生后,神经网络可以看作是一个由若干无向树 \(\{T_1(V_1, E_1), T_2(V_2, E_2),\ldots T_m(V_m, E_m)\}\) 构成的森林。随着火星人年龄的增长,神经连接的数量也不断增长。初始时,神经网络中生长的连接 \(E^\ast = \emptyset\)。神经网络根据如下规则生长:

- 如果节点 \(u \in V_i, v \in V_j\) 分别属于不同的无向树 \(T_i\) 和 \(T_j\ (i \neq j)\),则 \(E^\ast\) 中应当包含边 \((u, v)\)。

最终,在不再有神经网络连接可能生长后,神经网络之间的节点连接可以看成是一个无向图 \(G(V,E)\),其中

\[V=V_1\cup V_2\cup \ldots \cup V_m,E=E_1\cup E_2\cup \ldots \cup E_m\cup E^\ast
\]

火星人的决策是通过在 \(G(V, E)\) 中建立环路完成的。针对不同的外界输入,火星人会建立不同的神经连接环路,从而做出不同的响应。为了了解火星人行为模式的复杂性,JYY 决定计算 \(G\) 中哈密顿回路的数量

\(G(V, E)\) 的哈密顿回路是一条简单回路,从第一棵树的第一个节点出发,恰好经过 \(V\) 中的其他节点一次且仅一次,并且回到第一棵树的第一个节点。

输入格式

第一行读入 \(m\),表示火星人神经网络初始时无向树的数量。

接下来输入有 \(m\) 部分,第 \(i\) 部分描述了树 \(T_i\)。

对于 \(T_i\),输入的第一行是树 \(T_i(V_i, E_i)\) 中节点的数量 \(k_i\)。假设 \(V_i = \{v1_, v_2,\ldots ,v_{k_i}\}\)。

接下来 \(k_{i} - 1\) 行,每行两个整数 \(x, y\),表示该树节点 \(v_x, v_y\ (1 \le x, y \le k_i)\) 之间有一条树边,即 \((v_x, v_y) \in E_i\)。

输出格式

因为哈密顿回路的数量可能很多,你只需要输出一个非负整数,表示答案对 \(998244353\) 取模后的值。

数据范围与提示

\(\sum_{i=1}^m k_i\le 5\times 10^3,m\leq 300\)

\(\\\)

一个合法情况可以这样来看:将第\(i\)棵树分为\(k_i\)条不相交链,且这些链的并是第\(i\)棵树,然后将这些链排列起来,相邻的链不能来自同一颗树。特别地,第\(1\)棵树的\(1\)号点所在的链要拆成两半分别放在排列的两端。

首先对于每颗树,\(DP\)出来选\(i\)条链的方案数,这个\(DP\)就设\(f_{i,j,0/1/2}\)表示以\(i\)为根的子树中,\(i\)所在的链点数为\(1\)/点数\(>1\)且没有分叉/有分叉的方案数。每种方案还要\(*2^k\),其中\(k\)是点数\(>1\)的链的数量(因为正反都可以)。

接着考虑用容斥来算答案。设\(S_{i,j}\)表示将\(i\)个元素分为\(j\)个排列的方案数。这个可以考虑先\(DP\)出“将长度为\(i\)的序列分成\(j\)段”的方案数,再\(*i!\)。对于第\(m\)棵树,有\(n\)个点,假设我们选出了\(i\)条链的方案数为\(G_i\)。我们要强制有\(j\)对链是相邻的,那么答案是\(S_{i,i-j}*G_i\),容斥系数是\((-1)^{j}\)。于是我们可以预处理出\(H\)数组,\(H_i\)表示选出来的链有\(i\)的系数:

\[H_i=\sum_{j=i}^n (-1)^{j-i} G_i*S_{i,j}
\]

注意第\(1\)棵树第\(1\)个点要特殊处理。最后在将每颗树选出来的许多连续段随便排列,这个可以用背包实现。

代码:

#include<bits/stdc++.h>
#define ll long long
#define M 305
#define N 5005 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} const ll mod=998244353;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
}
const ll inv2=mod+1>>1;
int m;
int n;
struct road {int to,nxt;}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;} ll fac[N],ifac[N];
ll C(int n,int m) {return n<m?0:fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
ll f[3][N][N];
ll tem[3][N];
int size[N];
ll F[305][N],G[N],H[N];
void Init() {
for(int i=1;i<=n;i++) h[i]=0;
cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[0][i][j]=f[1][i][j]=f[2][i][j]=0;
for(int i=1;i<=n;i++) size[i]=0;
} void dfs(int v,int fr) {
f[0][v][1]=1;
size[v]=1;
for(int i=h[v];i;i=s[i].nxt) {
int to=s[i].to;
if(to==fr) continue ;
dfs(to,v);
for(int j=1;j<=size[v]+size[to];j++) tem[0][j]=tem[1][j]=tem[2][j]=0;
for(int j=1;j<=size[v];j++) {
for(int k=1;k<=size[to];k++) {
(tem[0][j+k]+=f[0][v][j]*(f[0][to][k]+f[1][to][k]+f[2][to][k]))%=mod;
(tem[1][j+k-1]+=f[0][v][j]*f[0][to][k]*2)%=mod;
(tem[1][j+k-1]+=f[0][v][j]*f[1][to][k])%=mod; (tem[1][j+k]+=f[1][v][j]*(f[0][to][k]+f[1][to][k]+f[2][to][k]))%=mod;
(tem[2][j+k-1]+=f[1][v][j]*f[0][to][k])%=mod;
(tem[2][j+k-1]+=f[1][v][j]*f[1][to][k]%mod*inv2)%=mod; (tem[2][j+k]+=f[2][v][j]*(f[0][to][k]+f[1][to][k]+f[2][to][k]))%=mod;
}
}
for(int j=1;j<=size[v]+size[to];j++) {
f[0][v][j]=tem[0][j];
f[1][v][j]=tem[1][j];
f[2][v][j]=tem[2][j];
}
size[v]+=size[to];
}
} int tot;
int S[N][N];
void pre(int n) {
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
ifac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
S[0][0]=1;
for(int j=1;j<=n;j++) {
ll pre=0;
for(int i=j;i<=n;i++) {
(pre+=S[i-1][j-1])%=mod;
S[i][j]=pre;
}
}
for(int i=0;i<=n;i++) {
for(int j=0;j<=i;j++) {
S[i][j]=S[i][j]*fac[i]%mod;
if(i-j&1) S[i][j]=(mod-S[i][j])%mod;
}
}
} int main() {
pre(5000);
m=Get();
for(int now=1;now<=m;now++) {
n=Get();
Init();
for(int j=1;j<n;j++) {
int a=Get(),b=Get();
add(a,b),add(b,a);
}
dfs(1,0);
if(now==1) {
for(int i=1;i<=n;i++) {
G[i]=(f[0][1][i]+f[1][1][i]+f[2][1][i])%mod;
}
tot+=n+1;
F[1][2]=G[1];
for(int i=1;i<n;i++) {
for(int j=1;j<=i;j++) {
(F[1][j+2]+=G[i+1]*S[i][j])%=mod;
(F[1][j+1]+=(mod-1)*G[i+1]%mod*S[i][j]*2)%=mod;
if(j>1) (F[1][j]+=G[i+1]*S[i][j])%=mod;
}
}
} else {
for(int i=1;i<=n;i++) {
G[i]=(f[0][1][i]+f[1][1][i]+f[2][1][i])%mod;
}
for(int i=0;i<=n;i++) H[i]=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=i;j++) {
(H[j]+=G[i]*S[i][j])%=mod;
}
}
for(int j=2;j<=tot;j++) {
for(int k=1;k<=n;k++) {
(F[now][j+k]+=F[now-1][j]*H[k]%mod*C(j-2+k,k))%=mod;
}
}
tot+=n;
}
}
ll Ans=0;
for(int i=2;i<=tot;i++) (Ans+=F[m][i])%=mod;
cout<<Ans;
return 0;
}

Loj #3102. 「JSOI2019」神经网络的更多相关文章

  1. 【LOJ】#3102. 「JSOI2019」神经网络

    LOJ#3102. 「JSOI2019」神经网络 首先我们容易发现就是把树拆成若干条链,然后要求这些链排在一个环上,同一棵树的链不相邻 把树拆成链可以用一个简单(但是需要复杂的分类讨论)的树背包实现 ...

  2. LOJ #3103. 「JSOI2019」节日庆典

    题意 给定字符串 \(S\) ,对于 \(S\) 的每个前缀 \(T\) 求 \(T\) 所有循环同构串的字典序最小的串,输出其起始下标.(如有多个输出最靠前的) \(|S| \le 3 \times ...

  3. LOJ3102. 「JSOI2019」神经网络 [DP,容斥,生成函数]

    传送门 思路 大部分是感性理解,不保证完全正确. 不能算是神仙题,但我还是不会qwq 这题显然就是求:把每一棵树分成若干条链,然后把链拼成一个环,使得相邻的链不来自同一棵树,的方案数.(我才不告诉你们 ...

  4. 【LOJ】#3103. 「JSOI2019」节日庆典

    LOJ#3103. 「JSOI2019」节日庆典 能当最小位置的值一定是一个最小后缀,而有用的最小后缀不超过\(\log n\)个 为什么不超过\(\log n\)个,看了一下zsy的博客.. 假如\ ...

  5. 【LOJ】#3101. 「JSOI2019」精准预测

    LOJ#3101. 「JSOI2019」精准预测 设0是生,1是死,按2-sat连边那么第一种情况是\((t,x,1) \rightarrow (t + 1,y,1)\),\((t + 1,y, 0) ...

  6. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  7. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  8. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  9. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

随机推荐

  1. jdbc:mysql:/// jdbc连接数据url简写方式

    正常情况下我们写jdbc连接本地mysql数据库的时候通常是这样写 jdbc:mysql:localhost:3306/数据库名 下面就是要提到的简单的方法 jdbc:mysql:///数据库名

  2. SQL学习笔记之 数据库基础(一)

    数据库基础 数据库系统的组成:由数据库,数据库管理软件,数据库管理员DBA,支持数据库系统的硬件和软件组成,其中数据库管理员是对数据库进行规划.设计.维护.和监视的专业管理人员,在数据库系统中起着非常 ...

  3. Delphi - 操作Excel数据公式的实现

    procedure TF_SMP_FT_NEW.RzBitBtn_StartToChangeClick(Sender: TObject); var i, j, ni, nj, iRows, iCol, ...

  4. 简单的python GUI例子

    写一个简单的界面很容易,即使是什么都不了解的情况下,这个文本转载了最简单的界面编写,下个文本介绍了TK的简单但具体的应用 在python中创建一个窗口,然后显示出来. from Tkinter imp ...

  5. Flask补充--threading.local对象

    目录 Local 局部变量 全局变量 使用threading.local() 自定义threading.local 函数版 面向对象版 通过setattr和getattr实现 每个对象有自己的存储空间 ...

  6. docker 安装与基本命令

    安装 Install Docker for Linux Download Docker for Mac Install Docker for Windows 镜像是docker三大核心概念中最重要的. ...

  7. Odoo学习笔记一:odoo初探

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11189194.html 一:Odoo架构 1:数据库服务器层:postgreSQL数据库服务器,用于存储所有 ...

  8. Windows 2003 IIS6.0下配置ASP+MySQL+PHP+ISAPI_Rewrite+Zend+Xcache

    windows 2003,自己买吧... 安装IIS6.0:安装系统后在"控制面板"->"添加或删除程序"->"添加/删除Windows组 ...

  9. Swagger从入门到放弃

    如何编写基于OpenAPI规范的API文档 简介 Swagger Swagger是一个简单但功能强大的API表达工具.支持的语言种类繁多 使用Swagger生成API,我们可以得到交互式文档,自动生成 ...

  10. 7、Topic

    Topics In the previous tutorial we improved our logging system. Instead of using a fanout exchange o ...