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)。

  • 相关阅读:
    vs.net 2005, 没有找到MSVCR80D.dll的完美解决方案
    C++内存布局从一个修改私有变量的问题想到的
    堆栈详解
    加载.x文件
    深入分析规则引擎
    高级着色语言HLSL入门(5)
    字符数组,字符指针,Sizeof总结
    C++ 隐式和显式 初始化,类型转换
    fread()和fwrite()函数分析
    结构体 对齐的问题
  • 原文地址:https://www.cnblogs.com/Scx117/p/8762133.html
Copyright © 2011-2022 走看看