Description

定义线图为把无向图的边变成点,新图中点与点之间右边当且仅当它们对应的边在原图中有公共点,这样得到的图。

定义弦图为不存在一个长度大于3的纯环,纯环的定义是在环上任取两个不相邻的点,它们之间都没有边,也就是不存在没有弦的环的无向图。

现在给出一棵n个点的树,你可以在上面添加任意多条边(不能重边),要求得到的图的线图是弦图,求加边的方案数。

n<=200000

Solution

画图可以发现,一个无向图的线图是弦图的充要条件就是不存在长度大于3的环(不一定是纯环)

也就是说,我们加边只能加成三角形,并且加的边还不能交叉。

转化后的题意等价于将树上的边分成若干个集合,每个集合要么是单独一条边,要么是两条相邻的边,问方案数。

\(O(n^2)\)的暴力呼之欲出,我们记\(f[i][0/1]\)表示处理完\(i\)的子树,\(i\)向父亲的这条边是否与子树内的边组合了。

转移的时候,我们只需要知道所有儿子中有几个0,也就是有几条边需要我们来分配。

不妨再DP预处理出一个数组\(g[i][0/1]\),表示一个点下面挂着i条边,要分配集合,i的父亲边是否参与的方案数。

我们考虑每次新加一条边,要么自成集合,要么与之前的某一个组合,有

\(g[i][0]=g[i-1]+g[i-2][0]*(i-1),g[i][1]=g[i-1][0]*i\)

由于只要知道有几个0,这显然可以分治NTT优化,这样就做完了。

时间复杂度\(O(n\log^2 n)\)

Code

以下代码未经测试,完全不能保证正确性。

(惨遭证伪,请勿参考)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 200005
#define M 524288
using namespace std;
const int mo=998244353;
typedef long long LL;
int n,m1,fs[N],nt[2*N],dt[2*N];
LL f[N][2],g[N];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
void link(int x,int y)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
}
namespace poly
{
int len,st[N],le[N],a[M+1];
int u1[M+1],u2[M+1],l2[M+1],cf[20],wg[M+1],wi[M+1],ny[M+1],bit[M+1];
void init()
{
fo(i,0,18) l2[cf[i]=(1<<i)]=i;
fod(i,M-1,2) if(!l2[i]) l2[i]=l2[i+1];
wg[0]=1,wg[1]=ksm(3,(mo-1)/M);
ny[1]=1;
fo(i,2,M) wg[i]=(LL)wg[i-1]*wg[1]%mo,ny[i]=(-(LL)ny[mo%i]*(mo/i)%mo+mo)%mo;
}
void prp(int num)
{
int p=M/num,w=cf[l2[num]-1];
fo(i,0,num-1) bit[i]=(bit[i>>1]>>1)|((i&1)<<w),wi[i]=wg[i*p];
}
int inc(int x,int y) {x+=y;return(x>=mo)?x-mo:x;}
int dec(int x,int y) {x-=y;return(x<0)?x+mo:x;} void DFT(int *a,int num)
{
fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
for(int h=1,l=num>>1;h<num;h<<=1,l>>=1)
{
for(int j=0;j<num;j+=(h<<1))
{
int *x=a+j,*y=x+h,*w=wi,v;
for(int i=0;i<h;i++,x++,y++,w+=l)
{
v=(LL)(*x)*(*y)%mo;
*y=dec(*x,v),*x=inc(*x,v);
}
}
}
}
void IDFT(int *a,int num)
{
DFT(a,num);
fo(i,0,num-1) a[i]=(LL)a[i]*ny[num]%mo;
reverse(a+1,a+num);
} void doit(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
doit(l,mid),doit(mid+1,r);
int num=cf[l2[le[l]+le[mid+1]-1]];
prp(num);
memset(u1,0,4*num),memset(u2,0,4*num);
fo(i,0,le[l]-1) u1[i]=a[st[l]+i];
fo(i,0,le[mid+1]-1) u2[i]=a[st[mid]+i];
DFT(u1,num),DFT(u2,num);
fo(i,0,num-1) u1[i]=(LL)u1[i]*u2[i]%mo;
IDFT(u1,num);
le[l]+=le[mid+1]-1;
fo(i,0,le[l]-1) a[st[l]+i]=u1[i];
}
}
using namespace poly; void dfs(int k,int fa)
{
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa) dfs(p,k);
}
len=0;
int cnt=0;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa)
{
cnt++;
a[st[cnt]=len++]=f[p][1];
a[len++]=f[p][0];
le[cnt]=2;
}
}
if(!cnt) f[k][0]=1;
else
{
doit(1,cnt);
f[k][0]=f[k][1]=0;
fo(i,0,le[1]-1)
{
f[k][0]=(f[k][0]+(LL)a[i]*g[i])%mo;
if(i) f[k][1]=(f[k][1]+(LL)a[i]*g[i-1]%mo*(LL)i)%mo;
}
}
}
int main()
{
cin>>n;
fo(i,1,n-1)
{
int x,y;
scanf("%d%d",&x,&y);
link(x,y),link(y,x);
}
g[0]=1,g[1]=1;
fo(i,2,n) g[i]=(g[i-1]+g[i-2]*(i-1))%mo;
init();
dfs(1,0);
printf("%lld\n",f[1][0]);
}

【PKUSC2019】线弦图【计数】【树形DP】【分治FFT】的更多相关文章

  1. 【BZOJ5119】【CTT2017】生成树计数 DP 分治FFT 斯特林数

    CTT=清华集训 题目大意 有\(n\)个点,点权为\(a_i\),你要连接一条边,使该图变成一颗树. 对于一种连边方案\(T\),设第\(i\)个点的度数为\(d_i\),那么这棵树的价值为: \[ ...

  2. [SHOI2008]仙人掌图 II——树形dp与环形处理

    题意: 给定一个仙人掌,边权为1 距离定义为两个点之间的最短路径 直径定义为距离最远的两个点的距离 求仙人掌直径 题解: 类比树形dp求直径. f[i]表示i向下最多多长 处理链的话,直接dp即可. ...

  3. HDU5593 ZYB's Tree 树形DP +分治

    感觉其实就是树分治,一次BC的题,感觉这次题目质量比较高,仅代表蒟蒻的看法 一次DFS获取每个点到子树的距离不大于K的点的个数, 然后一遍BFS获取从每个点父亲不大于K的的个数,层层扩展,还是想说 其 ...

  4. CSU 1804 - 有向无环图 - [(类似于)树形DP]

    题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1804 Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 ...

  5. BZOJ4871 Shoi2017摧毁“树状图”(树形dp)

    设f[i][0/1/2/3/4/5]表示i子树中选一条链不包含根/i子树中选一条链包含根但不能继续向上延伸/i子树中选一条链可以继续向上延伸/选两条链不包含根/选两条链包含根但不能继续向上延伸/选两条 ...

  6. bzoj 4871: [Shoi2017]摧毁“树状图”【树形dp】

    做不来--参考https://www.cnblogs.com/ezyzy/p/6784872.html #include<iostream> #include<cstdio> ...

  7. 【LOJ565】【LibreOJ Round #10】mathematican 的二进制 DP 分治FFT

    题目大意 有一个无限长的二进制串,初始时它的每一位都为 \(0\).现在有 \(m\) 个操作,其中第 \(i\) 个操作是将这个二进制串的数值加上 \(2^{a_i}\).我们称每次操作的代价是这次 ...

  8. BZOJ 3456 城市规划 (组合计数、DP、FFT)

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3456 著名的多项式练习题,做法也很多,终于切掉了纪念 首先求一波递推式: 令\(F(n ...

  9. HDU 2242 连通分量缩点+树形dp

    题目大意是: 所有点在一个连通图上,希望去掉一条边得到两个连通图,且两个图上所有点的权值的差最小,如果没有割边,则输出impossible 这道题需要先利用tarjan算法将在同一连通分量中的点缩成一 ...

随机推荐

  1. 大数据之Hadoop完全分布式集群搭建

    1.准备阶段 1.1.新建三台虚拟机 Hadoop完全分市式集群是典型的主从架构(master-slave),一般需要使用多台服务器来组建.我们准备3台服务器(关闭防火墙.静态IP.主机名称).如果没 ...

  2. 继续:Ruby on Rails 简单了解

    一. 接着上一篇继续 1.限制微博的长度 在 Rails 中实现这种限制很简单,使用验证(validation)功能即可.要限制微博的长度最多为 140 个字符 (1).打开文件:app/models ...

  3. python cx_oracle 环境搭建

    背景说明: 之前的环境本来是可以用的,是另外一个项目(python27)需要的时候搭建的.新项目采用的是python36,安装的cx_oracle的版本是7,而环境中的Oracle客户端是11,导致p ...

  4. 前端Ajax通过设置 timeout 参数,轮询后台API

    因为我连接的数据库在台湾,相距较远,所以conn.Open()方法打开极慢.前端Ajax访问API时,API的数据还未返回,前端Ajax访问已经超时. 所以设置一个轮询,设置相隔多少秒之后进行一次查询 ...

  5. sql--ALTER

    ALTER TABLE 语句 ALTER TABLE 语句用于在已有的表中添加.修改或删除列. SQL ALTER TABLE 语法 如需在表中添加列,请使用下列语法: ALTER TABLE tab ...

  6. 109、Secret的使用场景 (Swarm16)

    参考https://www.cnblogs.com/CloudMan6/p/8082429.html   我们可以用secret管理任何敏感数据.这些敏感数据是容器在运行时需要的.同时我们又不想把这些 ...

  7. loj 6031「雅礼集训 2017 Day1」字符串

    loj 注意到每次询问串长度都是给定的,并且询问串长\(k*\)询问次数\(q<10^5\),所以这里面一个东西大的时候另一个东西就小,那么考虑对较小的下功夫 如果\(k\le \sqrt{n} ...

  8. Hyperledger Fabric(3)通道与组织

    1,通道的结构 通道是Fabric中非常重要的概念(类似微信群?),它实质是由排序节点划分和管理的私有原子广播通道,目的是对通道的信息进行隔离,使得通道外的实体无法访问通道内的信息,从而实现交易的隐私 ...

  9. Java内存泄漏真实案例

    内存泄漏:当不再需要一个对象时,垃圾收集器会回收它:如果不需要的对象一直在产生而不被收回,就称作“内存泄漏”. 以下为本人在工作中遇到的内存泄漏的案例: 1.对于大量的请求,使用了Executors. ...

  10. Delphi 变量的作用域