洛谷6516

简单的多项式题。
挖掘bfs图的性质。
显然,对于每条边\((x,y)\)(设\(d_x<d_y\)),\(d_x=d_y\)\(d_x+1=d_y\)
且每个点至少有一条边连向上一层。
\(c_i\)表示有多少个\(d=i\)
发现\(\sum C_{c_i}^2=v\)较大,所以求出连向相邻层的边的生成函数,然后枚举同一层的边取多少个即可。
同一层边取\(x\)个的方案是\(C_v^x\)
连向相邻层的边的生成函数可以把每一层\(i\)连向\(i+1\)的生成函数求出,然后分治fft计算。
由于每一层\(i\)必须向前面那一层连边,所以当连了\(<c_i\)条边,生成函数对应项是\(0\)
当连了\(=c_i\)条边时,生成函数对应项是\(c_{i-1}^{c_i}\)
当连了\(>c_i=t\)条边,则我们有额外自由的\(e=c_{i-1}*c_i-t\)条边。
方案数是\(\frac{e!}{(e-t)!}\)
\(s=\sum c_i*c_i+1\)
时间复杂度\(O(s\log_2^2s)\)
代码不知道为什么挂了。

#include<bits/stdc++.h>
using namespace std;
#define mo 998244353
#define N 500010
#define ll unsigned long long
#define pl vector<int>
int qp(int x,int y){
	int r=1;
	for(;y;y>>=1,x=1ll*x*x%mo)
		if(y&1)r=1ll*r*x%mo;
	return r;
}
int rev[N],v,le,w[N];
void deb(pl x){
	for(int i=0;i<x.size();i++)
		printf("%lld ",x[i]);
	puts("");
}
void init(int n){
	v=1;
	le=0;
	while(v<n)le++,v*=2;
	for(int i=0;i<v;i++)
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(le-1));
	int g=qp(3,(mo-1)/v);
	w[v/2]=1;
	for(int i=v/2+1;i<v;i++)
		w[i]=1ull*w[i-1]*g%mo;
	for(int i=v/2-1;~i;i--)
		w[i]=w[i*2];
}
void fft(int v,pl &a,int t){
	static unsigned long long b[N];
	int s=le-__builtin_ctz(v);
   	for(int i=0;i<v;i++)
   		b[rev[i]>>s]=a[i];
	int c=0;
	w[0]=1;
    for(int i=1;i<v;i*=2,c++)
    	for(int r=i*2,j=0;j<v;j+=r)
            for(int k=0;k<i;k++){
               	int tx=1ll*b[j+i+k]*w[k+i]%mo;
            	b[j+i+k]=b[j+k]+mo-tx;
            	b[j+k]+=tx;
            }
    for(int i=0;i<v;i++)
    	a[i]=b[i]%mo;
    if(t==0)return;
    int iv=qp(v,mo-2);
    for(int i=0;i<v;i++)
    	a[i]=1ull*a[i]*iv%mo;
    a.resize(v);
    reverse(a.begin()+1,a.end());
}
pl operator *(pl x,pl y){
	int s=x.size()+y.size()-1;
	if(x.size()<=20||y.size()<=20){
		pl r;
		r.resize(s);
		for(int i=0;i<x.size();i++)
			for(int j=0;j<y.size();j++)
				r[i+j]=(r[i+j]+1ll*x[i]*y[j])%mo;
		return r;
	}
	init(s);
	x.resize(v);
	y.resize(v);
	fft(v,x,0);
	fft(v,y,0);
	//deb(x);
	//deb(y);
	for(int i=0;i<v;i++)
		x[i]=1ll*x[i]*y[i]%mo;
	fft(v,x,1);
	x.resize(s);
	return x;
}
void inv(int n,pl &b,pl &a){
	if(n==1){
		b[0]=qp(a[0],mo-2);
		return;
	}
	inv((n+1)/2,b,a);
    static pl c;
	init(n*2);
	c.resize(v);
	b.resize(v);
    for(int i=0;i<n;i++)
		c[i]=a[i];
    fft(v,c,0);
    //deb(c);
	fft(v,b,0);
	//deb(b);
    for(int i=0;i<v;i++)
    	b[i]=1ll*(2ll-1ll*c[i]*b[i]%mo+mo)%mo*b[i]%mo;
    //deb(b);
    fft(v,b,1);  
   	b.resize(n);
   	//deb(b);
}
void ad(pl &x,pl y,int l){
	x.resize(max((int)x.size(),(int)y.size()+l));
	for(int i=0;i<y.size();i++)
		x[i+l]=(x[i+l]+y[i])%mo;
}
pl operator +(pl x,pl y){
	ad(x,y,0);
	return x;
}
pl iv(pl x){
	pl y;
	int n=x.size();
	y.resize(n);
	inv(n,y,x);
	y.resize(n);
	return y;
}
pl operator -(pl x,pl y){
	int s=max(x.size(),y.size());
	x.resize(s);
	y.resize(s);
	for(int i=0;i<s;i++)
		x[i]=(x[i]-y[i]+mo)%mo;
	return x;
}
pl qd(pl x){
	pl y;
	int n=x.size();
	y.resize(n-1);
	//deb(x);
	for(int i=0;i<n-1;i++)
		y[i]=1ll*x[i+1]*(i+1)%mo;
	//deb(y);
	return y;
}
pl jf(pl x){
	int n=x.size();
	pl y;
	y.resize(n+1);
	for(int i=1;i<=n;i++)
		y[i]=1ll*x[i-1]*qp(i,mo-2)%mo;
	return y;
}
pl ln(pl x){
	int n=x.size();
	pl y=qd(x),z=iv(x);
	y=y*z;
	y=jf(y);
	y.resize(n);
	return y;
}
char bf[100];
void wr(int x){
	if(!x){
		putchar('0');
		putchar(' ');
		return;
	}
    int ct=0;
    while(x){
        bf[++ct]=x%10;
        x/=10;
    }
    for(int i=ct;i;i--)
        putchar(bf[i]+'0');
    putchar('\n');
}
void gt(int n,pl &y,pl x){
	if(n==1){
		y.resize(1);
		y[0]=1;
		return;
	}
	gt((n+1)/2,y,x);
	pl z=x,a;
	z.resize(n);
	y.resize(n);
	a.resize(1);
	a[0]=1;
	y=y*(a-ln(y)+z);
	y.resize(n);
}
pl ep(pl x){
	pl y;
	int n=x.size();
	gt(n,y,x);
	return y;
}
void put(pl a){
	for(int i=0;i<a.size();i++)
		printf("%lld ",a[i]);
	puts("");
}
pl a[N];
int d[N],c[N],jc[N],ij[N];
int cc(int y,int x){
	return 1ll*jc[y]*ij[x]%mo*ij[y-x]%mo;
}
pl fz(int l,int r){
	if(l==r)
		return a[l];
	int md=(l+r)/2;
	return fz(l,md)*fz(md+1,r);
}
pl qp(pl x,int y){
	if(!y){
		pl r;
		r.resize(x.size());
		r[0]=1;
		return r;
	}
	int po=-1,l=x.size(),va;
	for(int i=0;i<l;i++)
		if(x[i]){
			po=i;
			break;
		}
	if(po==-1)return x;
	va=x[po];
	int vv=qp(va,mo-2);
	for(int i=min(po,l-1);i<l;i++)
		x[i-po]=1ll*x[i]*vv%mo;
	x.resize(l-min(po,l));
	x=ln(x);
	y%=mo;
	for(int i=0;i<x.size();i++)
		x[i]=1ll*x[i]*(y%mo)%mo;
	x=ep(x);
	po*=y;
	va=qp(va,y);
	x.resize(l);
	for(int i=l-1;i>=po;i--)
		x[i]=1ll*x[i-po]*va%mo;
	for(int i=min(l-1,po-1);~i;i--)
		x[i]=0;
	return x;
}
signed main(){
	int n,m,ans=0,va=0;
	scanf("%d%d",&n,&m);
	jc[0]=ij[0]=1;
	for(int i=1;i<N;i++)
		jc[i]=1ll*jc[i-1]*i%mo;
	ij[N-1]=qp(jc[N-1],mo-2);
	for(int i=N-1;i;i--)
		ij[i-1]=1ll*ij[i]*i%mo;
	int md=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&d[i]);
		c[d[i]]++;
		md=max(md,d[i]);
	}
	for(int i=0;i<n;i++)
		va+=(c[i]-1)*c[i]/2;
	pl x,tp;
	x.resize(va+1);
	for(int i=0;i<=va;i++)
		x[i]=cc(va,i);
	for(int i=1;i<=md;i++){
		tp.resize(c[i-1]+1);
		for(int j=0;j<=c[i-1];j++)
			tp[j]=cc(c[i-1],j);
		tp[0]=(tp[0]-1+mo)%mo;
		tp.resize(c[i-1]*c[i]+1);
		tp=qp(tp,c[i]);
		a[i]=tp;
	}
	pl vv=fz(1,md);
	vv=vv*x;
	if(vv.size()>m)
		printf("%d",vv[m]);
	else
		puts("0");
}

一种更方便的解释:
相邻两层的生成函数是\(((1+x)^{t_i}-1)^{t_{i+1}}\)
\((1+x)^{t_i}-1\)是因为\(i+1\)层必须向\(i\)层连出边。
\(t_{i+1}\)是因为第\(i+1\)层有\(t_{i+1}\)个点。
然而这样子这道题就太水了。
考虑前面的式子:\(((1+x)^{t_i}-1)^{t_{i+1}}\),可以用ln,exp技巧。
但是由于\((1+x)^{t_i}-1\)常数项\(=0\)所以不方便ln。
发现不能用传统的方法转化。
考虑整体带入。注意到\(-(x^{t_i}-1)\)是付公主的背包中的式子。
于是令\(y=x+1,f(y)=-(1-y^{t_i})^{t_{i+1}}\)
\(ln((1-y^{t_i})^{t_{i+1}})=t_{i+1}ln(1-y^{t_i})\)
计算\(ln(1-y^{t_i})\)十分经典。
接下来考虑令\(x++\)得到真实的答案。
\(\sum _{i\geq 0}(x+1)^i=\sum_{i\geq 0}\sum_{j\geq 0}C_j^i=\sum_{j\geq 0}x^j\sum_{i\geq 0}g_{i+j}\frac{(i+j)!}{i!j!}=\sum_{j\geq 0}\frac{x^j}{j!}\sum_{i\geq 0}g_{i+j}\frac{(i+j)!}{i!}\)
这样子就能用减法卷积计算了。

posted @ 2021-02-24 16:36  celerity1  阅读(53)  评论(0编辑  收藏  举报