【题解】CF917D Stranger Trees(prufer序列+二项式反演)
考虑有一个东西叫做\(prufer\)序列,然后个东西叫做图联通方案数。
然后我们考虑先算一下至少\(k\)条边在方案里的方案数,我们可以用树形dp
\(dp(i,j,k)\)表示对于\(i\)节点及其子树,共有\(j\)条边被选中,且和\(i\)共有\(k\)个点是在一个联通块里的\(\prod siz[]\)之和。转移很简单啦。(但是CF的内存访问极慢!要用对内存友好的写法)
通过\(dp()\)可以很方便的求出来是\(f(k)\)表示至少有\(k\)条边在方案里的方案数。
我们设\(g(k)\)为答案,那么考虑\(f(k)\)和\(g(k)\)的关系
值得注意的事情是,由于我们是在树上选择边,所以我们钦定选择的边是不会成环的,也就是说任何一个选树边的方案都是合法的,所以我们有
\[ f(k)=\sum_{j\ge k} {j\choose k} g(j) \]
根据二项式反演直接得到
\[ g(k)=\sum_{j\ge k}{j\choose k}(-1)^{k-j}f(j) \]
//@winlere
#include
#include
#include
#include
#include
#include
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=105;
const int mod=1e9+7;
int dp[maxn][maxn][maxn],n,siz[maxn],ans[maxn];
int invs[maxn*maxn],c[maxn][maxn];
int inv[maxn][maxn*maxn];
vectore[maxn];
void add(int fr,int to){e[fr].push_back(to);e[to].push_back(fr);}
int MOD(const int&x){return x>=mod?x-mod:x;}
int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
int MOD(const int&x,const int&y,const int&z,const int&b){return 1ll*x*y%mod*b%mod*z%mod;}
int ksm(const int&ba,const int&p){
int ret=1;
for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
if(t&1) ret=MOD(ret,b);
return ret;
}
void pre(const int&n){
invs[1]=1;
for(int t=2;t<=n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]),assert(MOD(t,invs[t])==1);
for(int t=n+1;t<=n*n;++t) invs[t]=MOD(mod-mod/t,invs[mod%t]);
for(int t=1;t<=n*n;++t)
for(int i=1;i<=n;++i)
inv[i][t]=MOD(i,invs[t]);
for(int t=0;t<=n;++t)
for(int i=c[t][0]=1;i<=t;++i)
c[t][i]=MOD(c[t-1][i-1]+c[t-1][i]);
//cerr<<"c[5][2]="<=0&&k>p) s=(s+1ll*dp[now][i-j-1][k-p]*dp[t][j][p]%mod*inv[k][(k-p)*p])%mod;
//dont connect
if(i>=j) s=(s+1ll*dp[now][i-j][k]*dp[t][j][p])%mod;
}
}
}
}
}这种写法很不优秀,内存访问非常不连续!
*/
for(int i=0;i