洛谷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!}\)
这样子就能用减法卷积计算了。