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

这篇博客介绍了Topo Counting问题,即在有向无环图DRG中计算拓扑序数。通过分析N=4的实例,提出了将问题分解为子图并使用动态规划求解的方法。由于原始的三维DP导致超时,作者进一步探讨了如何压缩到二维DP以优化解决方案。
摘要由CSDN通过智能技术生成

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 <j 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 <M \le 2 ^ {30}) NM(1N3000NN2<M230),并且 M M M是质数。

输出描述:

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

样例:

样例输入1:

2 1073741789

样例输出1:

31

样例输入2:

3 1073741789

样例输出2:

7954100

思路:

首先我们画一张图 ( n = 4 ) (n=4) (n=4)(爬ppt(官方)的图)
在这里插入图片描述
从中我们可以发现:所有的子图都连向了第一号子图,仔细观察就会发现这个图就像一个晒肉架子… 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的维度:
如果在一个完整的图中只删去了一个点,我们只需要一维:
在这里插入图片描述
如果只删去第一行的,那么只需要两维:
在这里插入图片描述
如果把第二行也删了,也只需要两维:
在这里插入图片描述
这样我们就成功的把 d p dp dp压成了二维。

A C AC AC C o d e Code Code:

#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]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值