题解 奇怪的拆分
首先发现答案为 \(\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;
}