2020暑期牛客多校训练营第七场(G)Topo Counting

Topo Counting

原题请看这里

题目描述:

给定一种有向无环图———的晒肉架图 ( D R G ) (DRG) (DRG),由唯一参数 N N N控制。 D R G DRG DRG包含 N N N组顶点。第 i i i V i V^i Vi包含 2 N 2N 2N个顶点: V 1 i , V 2 i , ⋯ , V 2 N i V^i_1,V ^ i_2,\cdots,V ^ i_ {2N} V1iV2iV2Ni
D R G DRG DRG中有两种类型的边:组内边(每组内的边)和内部-组边(组之间的边)。
组内边缘:对于第 i i i组,存在以下组内边缘:
( V j i , V j + N i ) (V ^ i_j,V ^ i_ {j+N}) (VjiVj+Ni),对于所有整数 j j j使得 1 ≤ j ≤ N 1\le j\le N 1jN
( V j i , V j + 1 i ) (V ^ i_j,V ^ i_ {j+1}) (VjiVj+1i),对于所有整数 j j j使得 1 ≤ j ≤ N − 1 1\le j\le N-1 1jN1 N + 1 ≤ j ≤ 2 N − 1 N+1 \leq j \le 2N-1 N+1j2N1
组间边缘:组边缘存在:
( V i + N 1 , V 1 i + 1 ) (V ^ 1_ {i + N},V ^ {i + 1} _1) (Vi+N1V1i+1),对于所有整数 i i i使得 1 ≤ i ≤ N − 1 1 \le i \le N-1 1iN1
( V i 1 , V 1 + N i ) (V ^ 1_ {i},V ^ {i} _ {1 + N}) (Vi1V1+Ni),对于所有整数 i i i使得 2 ≤ i ≤ N 2 \le i \le N 2iN
现在我们想知道以 N N N为参数的 D R G DRG DRG的拓扑序数。
有向图 G = ( V , E ) G=(V,E) G=(VE)的拓扑顺序是 V ( G ) V(G) V(G)的所有顶点的排列 v p 1 , v p 2 , ⋯ , v p ∣ V ( G ) ∣ ) v_ {p_1},v_ {p_2},\cdots,v_ {p_ {| V(G)|}} ) vp1vp2vpV(G))使得对于所有 i < j i i<j ( v p j , v p i ) ∉ E ( G ) (v_ {p_j},v_ {p_i}) \not \in E(G) (vpjvpi)E(G)
为了避免计算巨大的整数,请对答案取模 M M M

输入描述:

输入仅包含两个整数 N , M ( 1 ≤ N ≤ 3000 , N ∗ N ∗ 2 < M ≤ 2 30 ) N,M(1 \le N \le 3000,N * N * 2 NM(1N3000NN2<M230),并且 M M M是质数。

输出描述:

输出一个整数,表示答案。

样例:

样例输入1:

2 1073741789

样例输出1:

31

样例输入2:

3 1073741789

样例输出2:

7954100

思路:

首先我们画一张图 ( n = 4 ) (n=4) (n=4)(爬ppt(官方)的图)
2020暑期牛客多校训练营第七场(G)Topo Counting_第1张图片
从中我们可以发现:所有的子图都连向了第一号子图,仔细观察就会发现这个图就像一个晒肉架子… n n n唯一确定了这张图。
看到这张图,我们忽略图与图之间的连边就可以分开考虑,然后 d p dp dp求出所有子图 ( ( ( ) ) )的可能性,最后汇总即可,如果是分开的几个子图,设两个子图上的节点数为 d 1 d_1 d1, d 2 d_2 d2,拓扑序的可能性为 a a a b b b,那么两个子图的可能性就是 C d 1 + d 2 d 1 a b C^{d1}_{d1+d2}ab Cd1+d2d1ab,这是很好解决的,但是图中是连着的,所以我们要分几种情况考虑。
对于一个子图,进行拓扑时删掉的点的数量上面的肯定比下面的多,而拓扑限制只有与一号子图的第一条连边,不删掉这条边是无法得到下面这块“肉”的,所以我们的 d p dp dp需要维护的东西比较多:当前第二排架子删到了第几个;第一排架子删到了第几个;维护的“肉”还剩多少。这时你会发现:我们写出的 d p dp dp是一个三维的 d p dp dp,超时。
哇我辛辛苦苦求出来的居然超时呜呜呜呜
于是我们又想到如何压缩 d p dp dp的维度:
如果在一个完整的图中只删去了一个点,我们只需要一维:
2020暑期牛客多校训练营第七场(G)Topo Counting_第2张图片
如果只删去第一行的,那么只需要两维:

如果把第二行也删了,也只需要两维:
2020暑期牛客多校训练营第七场(G)Topo Counting_第3张图片
这样我们就成功的把 d p dp dp压成了二维。

A C AC AC C o d e Code Code:

#include 
#define ll long long
using namespace std;
const int MAXN = 3005;
const int MAXM = MAXN * MAXN * 2;
int n , mod , dp[ MAXN ][ MAXN ] , mul[ MAXM ] , inv[ MAXM ];
int ksm (int a, int b) {
    int ret = 1;
    while (b) {
        if(b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return ret;
}
int C (int n, int m) {
    if( n < m || m < 0 ) return 0;
    return 1ll * mul[ n ] % mod * inv[ m ] % mod * inv[ n - m ] % mod;
}
int Cat (int n, int m) { return ( C(n * 2 - m, n ) - C( n * 2 - m, n - m - 1 ) + mod ) % mod; }
int main () {
    scanf( "%d%d" , &n, &mod );
    mul[ 0 ] = dp[ 0 ][ 0 ] = 1;
    for ( int i = 1 ; i < MAXM ; ++i ) mul[ i ] = 1ll * mul[ i - 1 ] * i % mod;
    inv[ MAXM - 1 ] = ksm( mul[ MAXM - 1 ] , mod - 2 );
    for ( int i = MAXM - 2 ; ~ i ; --i ) inv[ i ] = 1ll * inv[ i + 1 ] * ( i + 1 ) % mod;
    for ( int i = 0 ; i < n ; ++i )
        for ( int j = 0 ; j < n ; ++j ) {
            if ( i == n - 1 && j == n - 1 ) continue;
            int left = 2 * n * n - min( i , j ) * n * 2 - i - j - 2;
            if ( j == i + 1)
                for ( int k = 0 ; k <= n; ++k )
                    dp[ i + 1 ][ j ] = ( dp[ i + 1 ][ j ] + 1ll * dp[ i ][ j ] * C( left - k , n * 2 - k ) % mod * Cat( n , k ) % mod ) % mod;
            else {
                dp[ i + 1 ][ j ] = ( dp[ i + 1 ][ j ] + dp[ i ][ j ] ) % mod;
                if ( i == j ) dp[ i ][ j + 1 ] = ( dp[ i ][ j + 1 ] + dp[ i ][ j ] ) % mod;
                else dp[ i ][ j + 1 ] = ( dp[ i ][ j + 1 ] + 1ll * dp[ i ][ j ] * C( left , n * 2 ) % mod * Cat( n , 0) % mod ) % mod;
            }
        }
    printf( "%d\n" , dp[ n - 1 ][ n - 1 ] );
}

你可能感兴趣的:(动态规划)