题意:有一张n个点的无向图,点有标号。求满足下列性质的图有多少个。
1.任意节点到1的最短路唯一。2.i的最短路长度<=i+1的最短路长度。3.所有点的度数给定,为2或3。
n<=400.
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=1e9+7; 4 const int inv2=5e8+4; 5 typedef long long ll; 6 const int N=405; 7 int n,jc[N],inv[N],sum2[N],sum3[N],d,g[N][N][N],f[N][N],ans; 8 void up(int &x,int y){x=((ll)x+y)%mod;} 9 int c(int x,int y){return (ll)jc[x]*inv[y]%mod*inv[x-y]%mod;} 10 int main() 11 { 12 scanf("%d",&n); 13 for (int i=1;i<=n;i++) 14 { 15 scanf("%d",&d);sum2[i]=sum2[i-1];sum3[i]=sum3[i-1]; 16 (d==2)?sum2[i]++:sum3[i]++; 17 if (i==1) f[d+1][d]=1; 18 } 19 jc[0]=jc[1]=inv[0]=inv[1]=1; 20 for (int i=2;i<=n;i++) jc[i]=(ll)jc[i-1]*i%mod,inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; 21 for (int i=2;i<=n;i++) inv[i]=(ll)inv[i-1]*inv[i]%mod; 22 g[0][0][0]=1; 23 for (int i=3;i<=n;i++) 24 for (int j=3;j<=i;j++) 25 up(g[0][0][i],(ll)g[0][0][i-j]*c(i-1,j-1)%mod*jc[j-1]%mod*inv2%mod); 26 for (int i=2;i<=n;i+=2) 27 up(g[0][i][0],(ll)(i-1)*g[0][i-2][0]%mod); 28 for (int j=1;j<=n;j++) 29 for (int k=1;k<=n-j;k++) 30 { 31 if (j>=2) up(g[0][j][k],(ll)(j-1)*g[0][j-2][k]%mod); 32 if (k) up(g[0][j][k],(ll)k*g[0][j][k-1]%mod); 33 } 34 for (int i=1;i<=n;i++) 35 for (int j=0;j<=n-i;j++) 36 for (int k=0;k<=n-i-j;k++) 37 { 38 if (j) up(g[i][j][k],(ll)j*g[i-1][j-1][k]%mod); 39 if (k) up(g[i][j][k],(ll)k*g[i-1][j+1][k-1]%mod); 40 } 41 for (int i=3;i<=n;i++)//考虑前i个点 42 { 43 for (int j=1;j<i-1;j++)//j个一层 44 for (int k=1;k<i-j;k++)//上一层k个 45 up(f[i][j],(ll)f[i-j][k]*g[j][sum2[i-j]-sum2[i-j-k]][sum3[i-j]-sum3[i-j-k]]%mod); 46 } 47 for (int i=1;i<n;i++) 48 up(ans,(ll)f[n][i]*g[0][sum2[n]-sum2[n-i]][sum3[n]-sum3[n-i]]%mod); 49 printf("%d ",ans); 50 return 0; 51 }
易错点:1.注意f[][]的初始化,设第一个点的度数为d,f[d+1][d]=1。
2.当dp转移式较为复杂时,考虑辅助数组来降低复杂度。
题解:dp
建立最短路树,发现同一层点的编号连续(因此可以区间dp)。由“最短路唯一”的性质可以得到,非树边一定是同层之间互连。
对于某一层的点,剩下度为1和2的点各有多少个可以互连要根据下一层有多少个儿子决定。
f[i][j]表示前i个点,j个点在当前的最后一层,最后一层没有互连的方案数。g[i][j][k]表示这一层有i个点,上一层有j个剩下度为1的点,k个剩下度为2的点。
显然f[i][j]=sigma(f[i-j][k]*g[j][k1][k2])。统计答案的时候就累加f[n][i]*g[0][k1][k2]即可。
预处理g:1.考虑如果只存在>=3个剩下度为2的点,那么必然构成环:枚举第k个元素所在的环,g[0][0][k]=sigma(g[0][0][k-l]*C(k-1,l-1)*(l-1)!/2);(最后一项是圆排列去重)
2.如果只存在剩下度为1的点,那么一定是两两连边:g[0][j][0]=(j-1)*g[0][j-2][0]。j一定是偶数。
3.如果度为1和2的点都有,而且没有下一层:任意选取一个度为1的点,它与度为1的点或度为2的点连边:g[0][j][k]=(j-1)*g[0][j-2][k]+k*g[0][j][k-1].(连度为1的,少了2个度为1的点;连度为2的点,少了一个度为2的点,度为1的点+1-1不变)
4.普遍情况:任意选取i个点中的一个,考虑和度为1的连边还是和度为2的点连边:g[i][j][k]=j*g[i-1][j-1][k]+k*g[i-1][j+1][k-1].
时间复杂度O(n^3)。