Topo Counting
题目描述:
给定一种有向无环图———的晒肉架图
,由唯一参数
控制。
包含
组顶点。第
组
包含
个顶点:
在
中有两种类型的边:组内边(每组内的边)和内部-组边(组之间的边)。
组内边缘:对于第
组,存在以下组内边缘:
,对于所有整数
使得
,对于所有整数
使得
或
组间边缘:组边缘存在:
,对于所有整数
使得
;
,对于所有整数
使得
现在我们想知道以
为参数的
的拓扑序数。
有向图
的拓扑顺序是
的所有顶点的排列
使得对于所有
,
为了避免计算巨大的整数,请对答案取模
。
输入描述:
输入仅包含两个整数 ,并且 是质数。
输出描述:
输出一个整数,表示答案。
样例:
样例输入1:
2 1073741789
样例输出1:
31
样例输入2:
3 1073741789
样例输出2:
7954100
思路:
首先我们画一张图
:(爬ppt(官方)的图)
从中我们可以发现:所有的子图都连向了第一号子图,仔细观察就会发现这个图就像一个晒肉架子…
唯一确定了这张图。
看到这张图,我们忽略图与图之间的连边就可以分开考虑,然后
求出所有子图
肉
的可能性,最后汇总即可,如果是分开的几个子图,设两个子图上的节点数为
,
,拓扑序的可能性为
,
,那么两个子图的可能性就是
,这是很好解决的,但是图中是连着的,所以我们要分几种情况考虑。
对于一个子图,进行拓扑时删掉的点的数量上面的肯定比下面的多,而拓扑限制只有与一号子图的第一条连边,不删掉这条边是无法得到下面这块“肉”的,所以我们的
需要维护的东西比较多:当前第二排架子删到了第几个;第一排架子删到了第几个;维护的“肉”还剩多少。这时你会发现:我们写出的
是一个三维的
,超时。
哇我辛辛苦苦求出来的居然超时呜呜呜呜
于是我们又想到如何压缩
的维度:
如果在一个完整的图中只删去了一个点,我们只需要一维:
如果只删去第一行的,那么只需要两维:
如果把第二行也删了,也只需要两维:
这样我们就成功的把
压成了二维。
:
#include <bits/stdc++.h>
#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 ] );
}