[LOJ6363]「地底蔷薇」

Description

古明地恋(koishi)和ICG姉貴(ichigo_aneki)是好朋友。

给定集合S,请你求出n个点的“所有极大点双连通分量的大小都在S内”的不同简单无向连通图的个数对 998244353 取模的结果。

点双连通分量:删去任意一个点后剩下的点依然保持连通的连通子图。
极大点双连通分量:一个点双连通分量,且不存在更大的点双连通分量包含自己。
极大点双连通分量的大小:指连通分量包含的点数。
两个简单无向图不同,当且仅当存在某条边(u,v)出现在了其中一个无向图,而没有出现在另一个无向图。

ICG:「えっと、それじゃあ、問題背景をください」
NYN:「ないです。」
ICG:「やめたらこの仕事!!!」
NYN:「しょうがないな。。。」
n<=1e5,∑ai<=1e5

Solution

我能说我是通过这道题知道cookie☆的吗
首先我们求出H(x)表示x个点的有根联通图数量,这个很好求(bzoj3456)
然后我们考虑一个有根联通图的构成,删去根之后会剩下许多联通块
每个联通块里面都有一些和根在同一个点双中间的点,将这些点双的边删去会得到许多有根联通图。
设bi表示i+1个点的点双个数,枚举有多少个这样的点,那么单个联通块的贡献可以写成 ∑ b i i ! H ( x ) i ∑{bi\over i!}H(x)^i i!biH(x)i记这个东西为B(H(x))
那么根据EGF的性质我们有对于整张图 H ( x ) = x e B ( H ( x ) ) H(x)=xe^{B(H(x))} H(x)=xeB(H(x))
B ( H ( x ) ) = l n H ( x ) x B(H(x))=ln{H(x)\over x} B(H(x))=lnxH(x)
H − 1 ( x ) H^{-1}(x) H1(x)表示H(x)的符合逆(即 H ( H − 1 ( x ) ) = x H(H^{-1}(x))=x H(H1(x))=x)
我们有 B ( x ) = l n x H − 1 ( x ) B(x)=ln {x\over H^{-1}(x)} B(x)=lnH1(x)x
直接用拉格朗日反演求B的复杂度无法承受我们需要另找其他方法
有个叫做扩展拉格朗日反演的东西
[ x n ] G ( H ( x ) ) = 1 n x n − 1 G ′ ( x ) ( x H − 1 ( x ) ) n [x^n]G(H(x))={{1\over n}}x^{n-1}G&#x27;(x)({x\over H^{-1}(x)})^n [xn]G(H(x))=n1xn1G(x)(H1(x)x)n
考虑构造 G ( H − 1 ( x ) ) = B ( x ) G(H^{-1}(x))=B(x) G(H1(x))=B(x)
那么 G ( x ) = l n H ( x ) x G(x)=ln{H(x)\over x} G(x)=lnxH(x)
G(x)是可以求出来的,进而可以用扩展拉格朗日反演求出每一个[x^n]B(x)
B ′ ( x ) = ∑ i − 1 ∈ S b i i ! x i B&#x27;(x)=\sum_{i-1\in S}{bi\over i!x^i} B(x)=i1Si!xibi
考虑答案的EGF F(x),根据上述推导有 F ( x ) = x e B ′ ( F ( x ) ) F(x)=xe^{B&#x27;(F(x))} F(x)=xeB(F(x))
那么[x^n]F(x)可以用一次拉格朗日反演求出
终于写出来了

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

const int N=3e5+5,Mo=998244353;

ll pwr(ll x,ll y) {
	ll z=1;
	for(;y;y>>=1,x=x*x%Mo)
		if (y&1) z=z*x%Mo;
	return z;
}

ll W[2][N],fact[N],inv[N];

void init(int b){
	for(int i=1;i<(1<<b);i<<=1){
		ll wn=pwr(3,(Mo-1)/(i<<1));
		for(int j=0;j<i;++j) W[1][i+j]=(j?wn*W[1][i+j-1]%Mo:1);
		wn=pwr(3,Mo-1-(Mo-1)/(i<<1));
		for(int j=0;j<i;++j) W[0][i+j]=(j?wn*W[0][i+j-1]%Mo:1);
	}
	int n=(1<<b)-1;
	fact[0]=1;fo(i,1,n) fact[i]=fact[i-1]*i%Mo;
	inv[n]=pwr(fact[n],Mo-2);fd(i,n-1,0) inv[i]=inv[i+1]*(i+1)%Mo;
}

void DFT(ll *a,int len,int flag) {
	if (flag==-1) flag=0;
	for(int i=0,j=0;i<len;++i){
		if(i<j) swap(a[i],a[j]);
		for(int k=len>>1;(j^=k)<k;k>>=1);
	}
	for(int i=1;i<len;i<<=1)
		for(int j=0;j<len;j+=(i<<1))
			for(int k=0;k<i;++k) {
				ll x=a[j+k],y=a[j+k+i]*W[flag][i+k]%Mo;
				a[j+k]=(x+y)%Mo;
				a[j+k+i]=(x-y)%Mo;
			}
	ll inv=pwr(len,Mo-2);
	if (!flag) for(int i=0;i<len;i++) a[i]=a[i]*inv%Mo;
}

ll c[N];

void get_Inv(ll *a,ll *b,int n) {
	if (n==1) {b[0]=pwr(a[0],Mo-2);return;}
	get_Inv(a,b,(n+1)>>1);
	int len=1;
	for(;len<n<<1;len<<=1);
	fo(i,0,n-1) c[i]=a[i];fo(i,n,len-1) c[i]=0;
	fo(i,(n+1)>>1,len-1) b[i]=0;
	DFT(c,len,1);DFT(b,len,1);
	fo(i,0,len-1) b[i]=(2*b[i]-b[i]*b[i]%Mo*c[i])%Mo;
	DFT(b,len,-1);
	fo(i,n,len-1) b[i]=0;
}

ll f[N],g[N];

void get_ln(ll *a,ll *b,int n) {
	fo(i,0,n-2) f[i]=a[i+1]*(i+1)%Mo;f[n-1]=0;
	get_Inv(a,g,n);
	int len=n<<1;
	fo(i,n,len-1) f[i]=g[i]=0;
	DFT(f,len,1);DFT(g,len,1);
	fo(i,0,len-1) f[i]=f[i]*g[i]%Mo;
	DFT(f,len,-1);
	fo(i,1,n-1) b[i]=f[i-1]*pwr(i,Mo-2)%Mo;
	b[0]=0;
}

ll h[N];

void get_exp(ll *a,ll *b,int n) {
	if (n==1) {b[0]=1;return;}
	get_exp(a,b,n>>1);
	get_ln(b,h,n);
	fo(i,0,n-1) h[i]=(a[i]-h[i]+Mo)%Mo;
	h[0]=(h[0]+1)%Mo;
	int len=n<<1;
	fo(i,n,len-1) h[i]=b[i]=0;
	DFT(b,len,1);DFT(h,len,1);
	fo(i,0,len-1) b[i]=h[i]*b[i]%Mo;
	DFT(b,len,-1);
	fo(i,n,len-1) b[i]=0;
}

ll p[N];

void get_pow(ll *a,int m,int n) {
	get_ln(a,p,n);
	fo(i,0,n-1) p[i]=p[i]*m%Mo;
	get_exp(p,a,n);
}

int n,m,len;
ll F[N],G[N],H[N],A[N],B[N],C[N];

void get_H() {
	fo(i,0,len-1) H[i]=pwr(2,(ll)i*(i-1)/2)*inv[i]%Mo;
	get_ln(H,H,len);
	fo(i,0,len-1) H[i]=H[i]*i%Mo;
}

void get_G() {
	fo(i,0,len-1) G[i]=H[i+1];
	get_ln(G,G,len);
	fo(i,0,len-1) G[i]=G[i+1]*(i+1)%Mo;
	G[len-1]=0;
}

ll get_B(int n) {
	for(len=1;len<=n;len<<=1);	
	fo(i,0,len-1) F[i]=H[i]*(-n)%Mo;
	get_exp(F,A,len);
	fo(i,0,len-1) C[i]=G[i];
	fo(i,len,len<<1) A[i]=C[i]=0;
	len<<=1;
	DFT(A,len,1);DFT(C,len,1);
	fo(i,0,len-1) A[i]=A[i]*C[i]%Mo;
	DFT(A,len,-1);
	return A[n-1]*pwr(n,Mo-2)%Mo;
}

ll get_F(int n) {
	for(len=1;len<=n;len<<=1);
	fo(i,0,len-1) B[i]=B[i]*n%Mo;
	get_exp(B,F,len);
	return F[n-1]*pwr(n,Mo-2)%Mo*fact[n-1]%Mo;
}

int main() {
	init(18);
	n=read();m=read();
	for(len=1;len<=n;len<<=1);
	get_H();get_G();
	fo(i,0,len-2) H[i]=H[i+1];H[len-1]=0;
	get_ln(H,H,len);
	fo(i,1,m) {
		int x=read()-1;
		B[x]=get_B(x);
	}
	printf("%lld\n",(get_F(n)+Mo)%Mo);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值