Gym102538A Airplane Cliques

Link
我们用\(T\)来表示题目给出的树,用\(S\)来表示枚举的点集。
不难发现\(G=T^x\)是一个弦图。(\(T^x\)指将\(T\)中距离不超过\(x\)的点两两连边得到的图)
然后我们任意固定一个点为根,然后将所有点按深度降序排序。事实上这个序列恰好是\(G\)的一个完美消除序列。
\(a_u\)表示在完美消除序列中在\(u\)后面的且和\(u\)相邻的点的个数,这可以通过点分治等途径求出。
固定\(|S|=k\),然后枚举\(S\)中在完美消除序列中最靠前的点\(u\),那么选择其它\(k-1\)个点的方案数就是\({a_u\choose k-1}\)
\(f_i\)\(|S|=i+1\)的个数,那么有\(f_i=\sum\limits_{j=1}^n{a_j\choose i}\)
\(t_x=\sum\limits_{i=1}^n[a_u=x]\),那么\(f_i=\sum\limits_{j=i}^{n-1}t_j{j\choose i}\)
利用NTT即可求出\(f\)

#include<cctype>
#include<cstdio>
#include<vector>
#include<cstring>
#include<numeric>
#include<algorithm>
const int N=1<<20|1,P=998244353;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
void dec(int&a,int b){a-=b,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int n,x,lim,cnt[N],dep[N],pre[N],id[N],fac[N],ifac[N],w[N],rev[N],f[N],g[N];std::vector<int>e[N];
namespace Divide_and_Conquer
{
    int root,mn,m1,m2,vis[N],size[N],s1[N],s2[N];
    void dfs(int u){size[u]=vis[u]=1;for(int v:e[u])if(!vis[v])dfs(v),size[u]+=size[v];vis[u]=0;}
    void find(int u,int s){int mx=0;vis[u]=1;for(int v:e[u])if(!vis[v])find(v,s),mx=std::max(mx,size[v]);if(vis[u]=0,(mx=std::max(mx,s-size[u]))<mn)root=u,mn=mx;}
    int get(int u,int d,int*s){int mx=d;if(vis[u]=1,u<=n)++s[d];for(int v:e[u])if(!vis[v])mx=std::max(mx,get(v,d+1,s));return vis[u]=0,mx;}
    void calc(int u,int d){if(d>x) return;cnt[u]+=s1[std::min(x-d,m1)]-s2[std::min(x-d,m2)],vis[u]=1;for(int v:e[u])if(!vis[v])calc(v,d+1);vis[u]=0;}
    void divide(int u)
    {
	mn=1e9,dfs(u),find(u,size[u]),u=root,m1=get(u,0,s1),std::partial_sum(s1,s1+m1+1,s1),cnt[u]+=s1[std::min(m1,x)],vis[u]=1;
	for(int v:e[u]) if(!vis[v]) m2=get(v,1,s2),std::partial_sum(s2,s2+m2+1,s2),calc(v,1),memset(s2,0,(m2+1)<<2);
	memset(s1,0,(m1+1)<<2);
	for(int v:e[u]) if(!vis[v]) divide(v);
    }
}
void dfs(int u,int fa)
{
    static int stk[N],top;
    dep[u]=dep[fa]+1,stk[++top]=u,pre[u]=stk[std::max(1,top-x)];
    for(int v:e[u]) if(v^fa) dfs(v,u);
    --top;
}
void init(int n)
{
    static int p=(lim=1<<(33-__builtin_clz(n)))/2,g=pow(3,(P-1)/lim);
    w[p]=1,fac[0]=1;
    for(int i=1;i<lim;++i) rev[i]=(rev[i>>1]>>1)|(i&1? p:0);
    for(int i=p+1;i<lim;++i) w[i]=mul(w[i-1],g);
    for(int i=p-1;i;--i) w[i]=w[i<<1];
    for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    ifac[n]=pow(fac[n],P-2);
    for(int i=n;i;--i) ifac[i-1]=mul(ifac[i],i);
}
void NTT(int*a,int f)
{
    if(!~f) std::reverse(a+1,a+lim);
    for(int i=1;i<lim;++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
    for(int i=1;i<lim;i<<=1) for(int j=0,d=i<<1;j<lim;j+=d) for(int k=0,x;k<i;++k) x=mul(a[i+j+k],w[i+k]),dec(a[i+j+k]=a[j+k],x),inc(a[j+k],x);
    if(!~f) for(int i=0,x=P-(P-1)/lim;i<lim;++i) a[i]=mul(a[i],x);
}
int main()
{
    n=read(),x=read(),init(n-1),memcpy(g,ifac,n<<2),std::reverse(g,g+n);
    for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].push_back(n+i),e[v].push_back(n+i),e[n+i].push_back(u),e[n+i].push_back(v);
    Divide_and_Conquer::divide(1),dfs(1,0),std::iota(id+1,id+n+1,1),std::sort(id+1,id+n+1,[](int i,int j){return dep[i]>dep[j];});
    for(int i=1;i<=n;++i) ++f[--cnt[pre[i]]];
    for(int i=0;i<n;++i) f[i]=mul(f[i],fac[i]);
    NTT(f,1),NTT(g,1);
    for(int i=0;i<lim;++i) f[i]=mul(f[i],g[i]);
    NTT(f,-1);
    for(int i=0;i<n;++i) printf("%d ",mul(ifac[i],f[i+n-1]));
}
posted @ 2020-04-12 21:17  Shiina_Mashiro  阅读(220)  评论(0编辑  收藏  举报