Samjia Graph (图计数, 多项式求逆 / 开根)

86 篇文章 0 订阅
6 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述

solution

考虑一种计数的方案, g ( n ) = ∑ x + y = n C ( n , x ) ⋅ 3 x y g(n) = \sum_{x+y=n} C(n,x) \cdot 3^{xy} g(n)=x+y=nC(n,x)3xy
x,y分别是左右的点数,3的几次方意思是黑 / 白 / 没有。

容易发现,对于一个有k个连通块的真正的黑白图,会在g(n)中贡献 2 k 2^k 2k次。

f ( n ) f(n) f(n)表示大小为n的连通黑白图数量。

这种带标号的东西,通常不考虑ogf而考虑egf。
设F,G分别是f,g的efg。

考虑答案的egf是什么。容易发现就是 e F e^F eF。式子自己展开一下就知道了。
然后"正好" G ( x ) = e 2 F ( x ) G(x) = e^{2F(x)} G(x)=e2F(x)
那么求出G,开个根就是F了。
顺便复习了一波多项式求逆和开根。

开根


T 2 ( x ) = A ( x ) ( m o d x n ) T^2(x) = A(x) (mod x^n) T2(x)=A(x)(modxn)
G 2 ( x ) = A ( x ) ( m o d x 2 n ) G^2(x) = A(x) (mod x^{2n}) G2(x)=A(x)(modx2n)
容易发现,
T ( x ) = G ( x ) ( m o d x n ) T(x) = G(x) (mod x^n) T(x)=G(x)(modxn)
G 2 ( x ) + T 2 ( x ) − 2 T ( x ) G ( x ) = 0 ( m o d x 2 n ) G^2(x) + T^2(x) - 2T(x)G(x)=0(mod x^{2n}) G2(x)+T2(x)2T(x)G(x)=0(modx2n)
A ( x ) + T 2 ( x ) − 2 T ( x ) G ( x ) = 0 ( m o d x 2 n ) A(x)+ T^2(x) - 2T(x)G(x)=0(mod x^{2n}) A(x)+T2(x)2T(x)G(x)=0(modx2n)
就可以求出G了。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define clear(st,ed) (memset(&st,0,sizeof(st) * ((&ed)-(&st))))
#define copy(tg,st,ed) (memcpy(&tg,&st,(&ed)-(&st)))
using namespace std;
typedef long long ll;
const int N = 1.5e6, mo = 998244353,inv3 = (mo + 1) / 3, inv2 = (mo + 1) / 2;
ll q,n=1e5;
ll o;
ll g[N],jc[N],njc[N],h[N],w[N],iw[N],M;

ll ksm(ll x,ll y) {
	ll ret = 1;
	for (; y; y>>=1) {
		if (y & 1) ret = ret * x % mo;
		x = x * x % mo;
	}
	return ret;
}

void dft(ll *a,ll sz,int sig) {
	for (int i = 1; i < sz; i++) 
		h[i] = (h[i>>1]>>1) + (i & 1) * (sz >> 1);
	for (int i = 0; i < sz; i++) if (h[i] < i) swap(a[i],a[h[i]]);
	for (int m = 1, g = 1; m < sz; m<<=1,g++) {
		for (int i = 0; i < sz; i+=m<<1) {
			for (int j = 0; j < m; j++) {
				ll A = a[i+j+m] * (sig == 1 ? w[(M>>g) * j] : iw[(M>>g) * j]) % mo;
				a[i+j+m] = (a[i+j] + mo - A) % mo;
				a[i+j] = (a[i+j] + A) % mo;
			}
		}
	}
	if (sig == -1) {
		ll ny = ksm(sz, mo - 2);
		for (int i = 0; i < sz; i++) a[i] = a[i] * ny % mo;
	}
}

ll G[N],tmp[N];
void inv(ll *G,ll *ori,int sz) {
	if (sz == 1) {
		G[0] = ksm(ori[0], mo - 2);
		return;
	}
	int csj = sz << 1;

	inv(G,ori,sz>>1);
	clear(G[sz>>1],G[csj]);
	dft(G,csj,1);

	for (int i = 0; i < csj; i++)
		tmp[i] = i < sz ? ori[i] : 0;
	dft(tmp,csj,1);

	for (int i = 0; i < csj; i++) 
		G[i] = G[i] * (2 - tmp[i] * G[i] % mo) % mo;

	dft(G,csj,-1);
	clear(G[sz],G[csj]);
}

ll invg[N];

void sqrt(ll *G,ll *ori,int sz) {
	if (sz == 1) {
		G[0] = 1; return;
	}
	int csj = sz << 1;
	sqrt(G, ori, sz >> 1);

	clear(invg[0],invg[csj]);
	inv(invg, G, sz);

	dft(G, csj, 1);
	dft(invg, csj, 1);

	for (int i = 0; i < csj; i++)
		tmp[i] = i < sz ? ori[i] : 0;
	dft(tmp, csj, 1);

	for (int i = 0; i < csj; i++) 
		G[i] = (G[i] * G[i] + tmp[i]) % mo * invg[i] % mo * inv2 % mo;

	dft(G, csj, -1);
	clear(G[sz],G[csj]);
}

void init() {
	M = 262144 * 2;
	ll ww = ksm(3, (mo - 1) / M);
	iw[0] = w[0] = 1;
	for (int i = 1; i < M; i++) w[i] = w[i-1] * ww % mo;

	ww = ksm(ww, mo - 2);
	for (int i = 1; i < M; i++) iw[i] = iw[i-1] * ww % mo;

	jc[0] = 1; for (int i = 1; i <= n; i++) jc[i] = jc[i-1] * i % mo;
	njc[n] = ksm(jc[n], mo - 2);
	for (int i = n - 1; ~i; i--)  njc[i] = njc[i+1] * (i+1) % mo;
}

ll od[N],ev[N],mer[N],rr[N];
ll d[N];

void getg() {
	for (ll i = 0; i <= n; i++) {
		if (i & 1) {
			od[i] = ksm(inv3, i * i >> 1) * njc[i] % mo;
		} else {
			ev[i] = ksm(inv3, i * i >> 1) * njc[i] % mo;
		}
	}
	dft(od, 262144, 1); dft(ev, 262144, 1);
	for (int i = 0; i < 262144; i++) {
		mer[i] = (ev[i] * ev[i] % mo 
				+ 2 * ev[i] * od[i] % mo 
				+ od[i] * od[i] % mo * inv3 % mo) % mo;
	}
	dft(mer, 262144, -1);
	for (ll i = 0; i <= n; i++) {
		mer[i] = mer[i] * ksm(3, i * i >> 1) % mo;
	}
}

int main() {
	freopen("graph.in","r",stdin);
	init();
	cin>>q;
	getg();
	sqrt(rr, mer, 262144);
	for (;q;q--){
		int u; scanf("%d",&u);
		printf("%lld\n",((jc[u] * rr[u] % mo) + mo) % mo);
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值