zoukankan      html  css  js  c++  java
  • loj6094 归乡迷途

    题意:有一张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)。

  • 相关阅读:
    PAT (Advanced Level) Practice 1055 The World's Richest (25 分) (结构体排序)
    PAT (Advanced Level) Practice 1036 Boys vs Girls (25 分)
    PAT (Advanced Level) Practice 1028 List Sorting (25 分) (自定义排序)
    PAT (Advanced Level) Practice 1035 Password (20 分)
    PAT (Advanced Level) Practice 1019 General Palindromic Number (20 分) (进制转换,回文数)
    PAT (Advanced Level) Practice 1120 Friend Numbers (20 分) (set)
    从零开始吧
    Python GUI编程(TKinter)(简易计算器)
    PAT 基础编程题目集 6-7 统计某类完全平方数 (20 分)
    PAT (Advanced Level) Practice 1152 Google Recruitment (20 分)
  • 原文地址:https://www.cnblogs.com/Scx117/p/8762133.html
Copyright © 2011-2022 走看看