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. SQLServer 跨服务器链接 Access数据库

    最近做了一个链接Access的实例,记录一笔. 如果你的Access数据库文件和SQLServer数据库在同一服务器上,可直接在数据库手动创建数据库链接 步骤如下: 打开新建链接,给你的链接起一个顺眼 ...

  2. ORACLE 求和(多列)

    SELECT SUM(列名),SUM(列名),SUM(列名),SUM(列名) FROM 表名

  3. Google开发者F12工具面板-network详解

    1 开发者工具面板    面板上包含了Elements面板.Console面板.Sources面板.Network面板.Performance面板.Memory面板.Application面板.Sec ...

  4. Python【day 13】内置函数01

    1.python3.6.2 一共有 68个内置函数2.分成6个大类 1.反射相关-4个 2.面向对象相关-9个 3.作用域相关--2个 1.globlas() #注意:最后是s,复数形式 查看全局作用 ...

  5. Spring登录实例

    Spring登录实例 项目结构 首先看一下整个项目的目录结构,如下: 导入Jar包 工欲善必先利其器,导入一下Jar包 配置文件 web.xml 配置 web.xml配置文件,如下: xmlns:xs ...

  6. Qt压缩和解压 zip

    zlib编译详见 https://blog.csdn.net/zhangxuechao_/article/details/85049711 下载quazip https://github.com/st ...

  7. hadoop 完全分布式集群搭建

    1.在伪分布式基础上搭建,伪分布式搭建参见VM上Hadoop3.1伪分布式模式搭建 2.虚拟机准备,本次集群采用2.8.3版本与3.X版本差别不大,端口号所有差别 192.168.44.10 vmho ...

  8. X264-视频帧的存取

    X264的编码器结构体x264_t中的子结构体字段frames包含了4个临时视频帧序列空间:current.next.unused和reference,分别保存当前编码帧.将编码帧序列.未处理原始视频 ...

  9. UML——Use Case Diagram(用例图)

    用例图主要用来描述角色以及角色与用例之间的连接关系.说明的是谁要使用系统,以及他们使用该系统可以做些什么.一个用例图包含了多个模型元素,如系统.参与者和用例,并且显示这些元素之间的各种关系,如泛化.关 ...

  10. 两种方式实现浅拷贝 for in实现和Object.assign({}, 对象1, 对象2);

    浅拷贝只拷贝对象的一层,如果对象的属性还是对象,那么user3和user4这两个对象对应的值都会发生改变 // 拷贝分为浅拷贝和深拷贝. // 浅拷贝的实现 通过for in实现 var user1 ...