其他分享
首页 > 其他分享> > CF1667E Centroid Probabilities

CF1667E Centroid Probabilities

作者:互联网

题面传送门
写了个NTT然后被一堆到现在还看不懂的线性做法吊起来打。
首先我们考虑直接对重心的充要条件下手:当前节点的孩子的子树不超过\(\frac{n}{2}\)且总和大于\(\frac{n}{2}\)
先考虑设\(g_i\)表示一颗\(i\)个点的树,且没有一个子树大小超过\(\frac{n}{2}\)的方案数。发现可以容斥,用总方案数\((i-1)!\)减去有一颗子树超过\(\frac{n}{2}\)的情况,容易发现这种子树只有一颗,那么可以枚举这颗子树的大小,然后得到\(g_i=\sum\limits_{j=\frac{n}{2}+1}^{i-1}{C_{i-1}^{j}\times (j-1)!\times (i-j-1)!}\)。
看上去可以直接NTT,但是我们可以展开发现\(g_i=\sum\limits_{j=\frac{n}{2}+1}^{i-1}{\frac{(i-1)!}{j}}\),然后就可以\(O(n)\)处理出来。
然后考虑每个点,接下来要考虑的就是这个点所在的子树大小,设方案数为\(f_i\),则有\(f_i=\sum\limits_{j=\frac{n}{2}+1}^{n-i+1}{C_{n-i}^{j-1}\times g_j\times (i-1)\times (n-j-1)!}\),展开以后直接NTT就好了。
时间复杂度\(O(n\log n)\)
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N ((1<<19)+5)
#define M (100+5)
#define K (1000+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k;ll Ans,F[N],G[N],B[N],D[N],frc[N],Inv[N],tr[N];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
const int g=3,Invg=mpow(3);
I void Init(int x){for(k=1;k<=2*x;k<<=1);for(int i=0;i<k;i++) tr[i]=(tr[i>>1]>>1)|((i&1)?(k/2):0);}
I void NTT(ll *A,int n,int Fl){
	int i,j,h;ll pus,key,now;for(i=0;i<n;i++) i>tr[i]&&(swap(A[i],A[tr[i]]),0);for(i=2;i<=n;i<<=1){
		for(key=mpow(Fl?g:Invg,(mod-1)/i),j=0;j<n;j+=i){
			for(now=1,h=j;h<j+i/2;h++) pus=now*A[h+i/2]%mod,A[h+i/2]=(A[h]-pus+mod)%mod,A[h]=(A[h]+pus)%mod,now=now*key%mod;
		}
	}if(Fl) return;int Invn=mpow(n);for(i=0;i<n;i++) A[i]=A[i]*Invn%mod;
}
int main(){
	freopen("1.in","r",stdin);
	int i,j;scanf("%d",&n);for(frc[0]=Inv[0]=i=1;i<=n;i++) frc[i]=frc[i-1]*i%mod,Inv[i]=mpow(frc[i]);Init(n);
	for(i=1;i<=n;i++)G[i]=frc[i-1]*(1-Ans+mod)%mod,i>n/2&&(Ans=(Ans+mpow(i))%mod);
	for(i=n/2+1;i<=n;i++) B[i]=Inv[i-1]*G[i]%mod*frc[n-i-1]%mod;for(i=0;i<=n;i++) D[i]=Inv[i]%mod;
	NTT(B,k,1);NTT(D,k,1);for(i=0;i<k;i++) B[i]=B[i]*D[i]%mod;NTT(B,k,0);for(i=1;i<=n;i++) printf("%lld ",i^1?B[n-i+1]*frc[n-i]%mod*(i-1)%mod:G[n]);
	/*for(i=1;i<=n;i++){if(i==1){printf("%lld ",(G[n]+mod)%mod);continue;}
		Ans=0;for(j=n/2+1;j<=n-i+1;j++) Ans+=Inv[j-1]*Inv[n-i-(j-1)]%mod*G[j]%mod*frc[n-j-1]%mod;printf("%lld ",(Ans%mod*frc[n-i]%mod*(i-1)%mod+mod)%mod);
	}*/
}

标签:子树,frac,Probabilities,NTT,times,int,Centroid,CF1667E,define
来源: https://www.cnblogs.com/275307894a/p/16180674.html