题解 奇怪的拆分

传送门

首先发现答案为 \(\sum\limits_{i=2}^n\begin{Bmatrix}n\\i\end{Bmatrix}(2i-1)!!\)
于是可以过 90 pts
然后考虑换做法:(复制题解)

官方题解

倒过来看合并就是拆分。一次拆分将集合拆成两个非空子集,可以对应到一棵每个节点代表 \(U\) 的一个子集的二叉树,二叉树的叶子节点即“小 Cu 拆出来的集合”。

设计 DP 状态 \(f_n\) 表示二叉树根节点是 \(\{1,2,\dots,n\}\),有多少种上述的二叉树。不过注意到一个点的二叉树是不算的,答案为 \(f_n-1\)

转移非常简单,要么是单个节点的二叉树,要么决策分给左子树 \(k\) 个元素;注意到左右子树交换顺序是本质相同的,所以乘上 \(\frac12\)

\[f_n=1+\frac12\sum_{k=1}^{n-1}\binom{n}{k}f_{k}f_{n-k} \]

出现了组合数,考虑 \(f_n\) 的指数型生成函数 \(F(x)\)。为了简化式子,假设 \(f_0=1\)

先把转移式写成完整的卷积形式:

\[f_n=1+\frac12\sum_{k=0}^{n}\binom nkf_kf_{n-k}-f_n+\frac{[n=0]}{2}\\ \frac{2f_n}{n!}=\frac{1+\frac12[n=0]}{n!}+\frac12\sum_{k=0}^{n}\frac{f_k}{k!}\cdot\frac{f_{n-k}}{(n-k)!}\\ 2F(x)=\frac12+e^x+\frac12F^2(x) \]

解方程得 \(F(x)=2-\sqrt{3-2e^x}\),多项式开根即可。

嗯嗯,题解写完了(逃

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 5000010
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
const ll mod=998244353, phi=mod-1, rt=3;
inline ll md(ll a) {return a>=mod?a-mod:a;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}

namespace force{
	int ans;
	map<vector<vector<int>>, bool> mp;
	void dfs2(vector<vector<int>> now, vector<vector<int>> s) {
		if (now.size()==1) {
			if (mp.find(s)==mp.end()) {
				mp[s]=1, ++ans;
				for (auto v:s) {cout<<"{"; for (auto it:v) cout<<it<<','; cout<<"\b}";} cout<<endl;
			}
			return ;
		}
		for (int i=0; i<now.size(); ++i) {
			for (int j=i+1; j<now.size(); ++j) {
				vector<vector<int>> t=now, ts=s;
				for (auto it:t[j]) t[i].pb(it);
				// t.erase(s.begin()+j);
				swap(t[j], t[t.size()-1]), t.pop_back();
				sort(t[i].begin(), t[i].end());
				ts.pb(t[i]);
				sort(t.begin(), t.end());
				sort(ts.begin(), ts.end());
				ts.erase(unique(ts.begin(), ts.end()), ts.end());
				dfs2(t, ts);
			}
		}
	}
	void dfs1(int u, vector<vector<int>> all) {
		if (u>n) {
			cout<<"all: "; for (auto v:all) {cout<<"{"; for (auto it:v) cout<<it<<','; cout<<"\b}";} cout<<endl;
			dfs2(all, all);
			return ;
		}
		for (auto& it:all) it.pb(u), dfs1(u+1, all), it.pop_back();
		vector<int> t; t.pb(u);
		all.pb(t); dfs1(u+1, all);
	}
	void solve() {
		dfs1(1, vector<vector<int>>());
		cout<<ans-1<<endl;
	}
}

namespace task1{
	ll s[5010][5010], f[N];
	void init() {
		s[0][0]=1; f[2]=1;
		for (int i=1; i<=5000; ++i)
			for (int j=1; j<=i; ++j)
				s[i][j]=(s[i-1][j-1]+s[i-1][j]*j)%mod;
		for (int i=3,k=3; i<=5000; ++i,k+=2) f[i]=f[i-1]*k%mod;
	}
	void solve() {
		// ll ans=0;
		// for (int i=2; i<=n; ++i) ans=(ans+s[n][i]*f[i])%mod;
		// printf("%lld\n", ans);
		for (int i=1; i<=n; ++i) cout<<s[n][i]<<' '; cout<<endl;
	}
}

namespace task2{
	int rev[N], bln, bct;
	ll fac[N], inv[N], f[N], g[N], h[N], t[N];
	inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
	void ntt(ll *a, int len, int op) {
		for (int i=0; i<len; ++i) if (i<rev[i]) swap(a[i], a[rev[i]]);
		ll w, wn, t;
		for (int i=1; i<len; i<<=1) {
			wn=qpow(rt, (op*phi/(i<<1)+phi)%phi);
			for (int j=0,step=i<<1; j<len; j+=step) {
				w=1;
				for (int k=j; k<j+i; ++k,w=w*wn%mod) {
					t=w*a[k+i]%mod;
					a[k+i]=(a[k]-t)%mod;
					a[k]=(a[k]+t)%mod;
				}
			}
		}
		if (op==-1) {
			ll inv=qpow(len, mod-2);
			for (int i=0; i<len; ++i) a[i]=a[i]*inv%mod;
		}
	}
	void solve() {
		fac[0]=fac[1]=1; inv[0]=inv[1]=1; h[2]=1;
		for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
		for (int i=0; i<=n; ++i) f[i]=(i&1?-1:1)*inv[i];
		for (int i=0; i<=n; ++i) g[i]=qpow(i, n)*inv[i]%mod;
		for (int i=3,k=3; i<=n; ++i,k+=2) h[i]=h[i-1]*k%mod;
		for (bln=1,bct=0; bln<=n*2; bln<<=1,++bct) ;
		for (int i=n+1; i<bln; ++i) f[i]=g[i]=0;
		for (int i=0; i<bln; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bct-1));
		ntt(f, bln, 1); ntt(g, bln, 1);
		for (int i=0; i<bln; ++i) f[i]=f[i]*g[i]%mod;
		ntt(f, bln, -1);
		// for (int i=0; i<=n; ++i)
		// 	for (int j=0; j<=n; ++j)
		// 		t[i+j]=(t[i+j]+f[i]*g[j])%mod;
		ll ans=0;
		for (int i=2; i<=n; ++i) ans=(ans+f[i]*h[i])%mod;
		printf("%lld\n", (ans%mod+mod)%mod);
		// for (int k=1; k<=n; ++k) {
		// 	ll ans=0;
		// 	for (int j=0; j<=k; ++j) ans=(ans+C(k, j)*(j&1?-1:1)*qpow(k-j, n))%mod;
		// 	cout<<(ans*inv[k]%mod+mod)%mod<<' ';
		// } cout<<endl;
	}
}

namespace task{
	ll fac[N], inv[N];
	int rev[N], que[N], lim;
	struct poly{
		vector<ll> a;
		int len() {return a.size();}
		void resize(int t) {a.resize(t);}
		inline ll& operator [] (int t) {return a[t];}
		void clr(int len) {for (int i=0; i<len; ++i) a[i]=0;}
		void cpy(poly& a, poly& b, int len) {for (int i=0; i<len; ++i) a[i]=b[i];}
		void mul(poly& a, poly& b, int len) {for (int i=0; i<len; ++i) a[i]=a[i]*b[i]%mod;}
		void adjust() {while (a.size() && !a.back()) a.pop_back();}
		void ntt(int len, int op) {
			int bct=ceil(log2(len));
			for (int i=0; i<len; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bct-1));
			for (int i=0; i<len; ++i) if (i<rev[i]) swap(a[i], a[rev[i]]);
			for (int i=0; i<len; ++i) a[i]=md(a[i]+mod);
			ll wn, w, t;
			for (int i=1; i<len; i<<=1) {
				wn=qpow(rt, (op*phi/(i<<1)+phi)%phi);
				for (int j=0,step=i<<1; j<len; j+=step) {
					w=1;
					for (int k=j; k<j+i; ++k,w=w*wn%mod) {
						t=a[k+i]*w%mod;
						a[k+i]=md(a[k]-t+mod);
						a[k]=md(a[k]+t);
					}
				}
			}
			if (op==-1) {
				ll inv=qpow(len, mod-2);
				for (int i=0; i<len; ++i) a[i]=a[i]*inv%mod;
			}
		}
		poly operator * (poly b) {
			poly t1=*this, t2=b;
			int len, n=t1.len()+t2.len()-1;
			for (len=1; len<=n; len<<=1) ;
			t1.resize(len); t2.resize(len);
			t1.ntt(len, 1); t2.ntt(len, 1);
			mul(t1, t2, len);
			t1.ntt(len, -1);
			t1.adjust();
			return t1;
		}
		poly qinv() {
			int lim;
			poly ans, t1, t2;
			for (lim=1; lim<a.size(); lim<<=1) ;
			resize(lim); ans.resize(lim); t1.resize(lim); t2.resize(lim);
			ans[0]=qpow(a[0], mod-2);
			for (int len=2; len<=lim; len<<=1) {
				cpy(t1, ans, len); cpy(t2, *this, len);
				t1.ntt(len, 1); t2.ntt(len, 1);
				mul(t1, t2, len); t1.ntt(len, -1);
				t1.clr(len>>1); cpy(t2, ans, len);
				t1.ntt(len, 1); t2.ntt(len, 1);
				mul(t1, t2, len); t1.ntt(len, -1);
				for (int i=len>>1; i<len; ++i) ans[i]=md(mod-t1[i]);
			}
			adjust(); ans.adjust();
			return ans;
		}
		poly sqrt() {
			int lim;
			poly ans, tem;
			for (lim=1; lim<a.size(); lim<<=1) ;
			resize(lim); ans.a.pb(1);
			for (int len=2; len<=lim; len<<=1) {
				ans.resize(len); tem.resize(len);
				cpy(tem, ans, len);
				ans.ntt(len, 1); mul(ans, ans, len); ans.ntt(len, -1);
				for (int i=0; i<len; ++i) ans[i]=md(ans[i]+a[i]), tem[i]=md(tem[i]<<1);
				ans=ans*tem.qinv();
				ans.resize(len);
			}
			adjust(); ans.adjust();
			return ans;
		}
	};
	void solve() {
		poly f;
		int T=read();
		for (int i=1; i<=T; ++i) lim=max(lim, que[i]=read());
		fac[0]=fac[1]=1; inv[0]=inv[1]=1; ++lim;
		for (int i=2; i<=lim; ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=2; i<=lim; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=2; i<=lim; ++i) inv[i]=inv[i-1]*inv[i]%mod;
		for (int i=0; i<=lim; ++i) f.a.pb(mod-2*inv[i]%mod);
		f[0]=(f[0]+3)%mod;
		f=f.sqrt();
		for (int i=0; i<=lim; ++i) f[i]=-f[i];
		// for (int i=0; i<=100; ++i) cout<<(f[i]%mod+mod)%mod<<' '; cout<<endl;
		// f[0]=(f[0]+2)%mod;
		f[0]=1;
		for (int i=1; i<=T; ++i) printf("%lld\n", ((f[que[i]]*fac[que[i]]-1)%mod+mod)%mod);
	}
}

signed main()
{
	freopen("set.in", "r", stdin);
	freopen("set.out", "w", stdout);

	// n=read();
	// force::solve();
	// task1::init(); task1::solve();
	// task2::solve();

	// int T=read();
	// // task1::init();
	// while (T--) {
	// 	n=read();
	// 	// task1::solve();
	// 	task2::solve();
	// }

	task::solve();

	return 0;
}
posted @ 2022-05-25 08:36  Administrator-09  阅读(2)  评论(0编辑  收藏  举报