[数学记录]AT2064 [AGC005F] Many Easy Problems

command_block

2021-01-12 10:43:57

Personal

**题意** : 给定一棵无根树。 定义 $f(k)$ 为 :对所有大小为 $k$ 的点集,求能够包含它的最小连通块大小之和。 对于 $k=1\dots n$ 分别求出 $f(k)$。 答案对 $924844033=2^{21}*3^2*7^2+1$(质数) 取模。 $n\leq 2\times 10^5$ ,时限$\texttt{5s}$。 ------------ 妙妙题。 考虑点 $u$ 对 $f(k)$ 的贡献 : 选出一个大小为 $k$ 的点集跨越 $u$ 的方案数。 对于 $u$ 而言,显然只需要考虑其各个分支的大小 $s_1,s_2,...,s_m$。 正着来有点不方便,考虑不跨越 $u$ 的方案数,那么必然只能完全在一个分支内。 不合法方案数为 $\sum\limits_{i=1}^m\dbinom{s_i}{k}$。总方案数为 $\dbinom{n}{k}$。 设 $c[s]$ 表示 $s$ 作为分支大小出现的次数,那么扣除的贡献即为 $\sum\limits_{i=1}^{n-1}c[s]\dbinom{s}{k}$。 这个拆开组合数随便卷一卷就是了。顺带一提,模数的原根是 $5$。 复杂度 $O(n\log n)$。 ```cpp #include<algorithm> #include<cstring> #include<cstdio> #include<vector> #define pb push_back #define ll long long #define ull unsigned long long #define MaxN 200500 #define Maxn 270500 using namespace std; const int mod=924844033,_G=5; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } const int invG=powM(_G); int tr[Maxn<<1]; void NTT(ll *g,bool op,int n) { static ull f[Maxn<<1],w[Maxn]={1}; for (int i=0;i<n;i++)f[i]=g[tr[i]]; for (int l=1;l<n;l<<=1){ ull tG=powM(op==1?_G:invG,(mod-1)/(l+l)),sav; for (int i=1;i<l;i++)w[i]=w[i-1]*tG%mod; for (int p=0;p<n;p+=(l+l)) for (int k=0;k<l;k++){ sav=w[k]*f[p|l|k]%mod; f[p|l|k]=f[p|k]+mod-sav; f[p|k]=f[p|k]+sav; } }if (op==0){ ll invn=powM(n); for (int i=0;i<n;i++) g[i]=f[i]%mod*invn%mod; }else for (int i=0;i<n;i++)g[i]=f[i]%mod; } ll fac[MaxN],ifac[MaxN]; ll C(int n,int m) {return fac[n]*ifac[m]%mod*ifac[n-m]%mod;} void Init(int n) { fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; ifac[n]=powM(fac[n]); for (int i=n;i;i--) ifac[i-1]=ifac[i]*i%mod; } vector<int> g[MaxN]; int n,siz[MaxN];ll c[Maxn<<1],f[Maxn<<1]; void dfs(int u) { siz[u]=1; for (int i=0,v;i<g[u].size();i++) if (!siz[v=g[u][i]]){ dfs(v); siz[u]+=siz[v]; c[siz[v]]++; } if (siz[u]<n)c[n-siz[u]]++; } int m; int main() { scanf("%d",&n);Init(n); for (int i=1,u,v;i<n;i++){ scanf("%d%d",&u,&v); g[u].pb(v);g[v].pb(u); }dfs(1); int m=1;while(m<n+n)m<<=1; for (int i=1;i<m;i++) tr[i]=(tr[i>>1]>>1)|((i&1)?(m>>1):0); for (int i=0;i<n;i++){ c[i]=c[i]*fac[i]%mod; f[i]=ifac[i]; }reverse(c,c+n); NTT(c,1,m);NTT(f,1,m); for (int i=0;i<m;i++)c[i]=c[i]*f[i]%mod; NTT(c,0,m); reverse(c,c+n);c[n]=0; for (int i=1;i<=n;i++) printf("%lld\n",((C(n,i)*n-c[i]*ifac[i])%mod+mod)%mod); return 0; } ```