BZOJ3456: 城市规划
BZOJ3456: 城市规划
https://lydsy.com/JudgeOnline/problem.php?id=3456
分析:
- 设\(f[n]\)表示\(n\)个点的答案, \(g[n]\)表示总方案数。
- 枚举和\(1\)连通的点的个数, \(f[n]=g[n]-\sum\limits_{i=1}^{n-1}\binom{n-1}{i-1}f(i)g(n-i)\)。
- 然后把式子划开就是一个多项式求逆了
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mod 1004535809
#define N 400050
typedef long long ll;
int qp(int x,ll y) {
int re=1; for(;y;y>>=1,x=ll(x)*x%mod) if(y&1) re=ll(re)*x%mod; return re;
}
#define INV(x) (qp(x,mod-2))
void ntt(int *a,int len,int flg) {
int i,j,k,t,w,wn,tmp;
for(i=k=0;i<len;i++) {
if(i>k) swap(a[i],a[k]);
for(j=len>>1;(k^=j)<j;j>>=1) ;
}
for(k=2;k<=len;k<<=1) {
t=k>>1;
wn=qp(3,(mod-1)/k);
if(flg==-1) wn=INV(wn);
for(i=0;i<len;i+=k) {
w=1;
for(j=i;j<i+t;j++) {
tmp=ll(a[j+t])*w%mod;
a[j+t]=(a[j]-tmp)%mod;
a[j]=(a[j]+tmp)%mod;
w=ll(w)*wn%mod;
}
}
}
if(flg==-1) {
t=INV(len);
for(i=0;i<len;i++) a[i]=ll(a[i])*t%mod;
}
}
int A[N],B[N],F[N],G[N],H[N],fac[N],inv[N],n,g[N];
void get_inv(int *a,int *b,int len) {
if(len==1) {
b[0]=INV(a[0]); return ;
}
get_inv(a,b,len>>1);
int t=len<<1,i;
for(i=0;i<len;i++) A[i]=a[i],B[i]=b[i];
ntt(A,t,1), ntt(B,t,1);
for(i=0;i<t;i++) B[i]=(2-ll(A[i])*B[i])%mod*B[i]%mod;
ntt(B,t,-1);
for(i=0;i<len;i++) b[i]=B[i];
}
int main() {
scanf("%d",&n);
int i;
for(fac[0]=i=1;i<=n;i++) fac[i]=ll(fac[i-1])*i%mod;
for(i=1;i<=n;i++) {
g[i]=qp(2,ll(i)*(i-1)/2);
}
inv[n]=INV(fac[n]);
for(i=n-1;i>=0;i--) inv[i]=ll(inv[i+1])*(i+1)%mod;
H[0]=1;
for(i=1;i<=n;i++) {
H[i]=ll(g[i])*inv[i]%mod;
G[i]=ll(g[i])*inv[i-1]%mod;
}
int len=1;
while(len<=n) len<<=1;
get_inv(H,F,len);
len<<=1;
ntt(F,len,1), ntt(G,len,1);
for(i=0;i<len;i++) F[i]=ll(F[i])*G[i]%mod;
ntt(F,len,-1);
printf("%lld\n",(ll(F[n])*fac[n-1]%mod+mod)%mod);
}