卧薪尝胆,厚积薄发。
      
    
            树
        
        
        Description:
求有多少棵树,满足
$DFS$
序为
$1,2,\dots,n$
且满足
$m$
个形如
$b$
在
$a$
子树的限制。
$1\leqslant n,m\leqslant 7000$
Solution:
先预处理一个
$r[i]$
表示点
$i$
在
$dfs$
上右端点至少是几,这个的求的过程就是求出必须在
$i$
的子树中的
$dfs$
最大的那个点,然后我们可以把
$dfs$
序列划分成若干个不相交的区间,这些区间会形成一个树结构,那么我们可以用类似树形
$DP$
的方式,具体来说设
$f[i][j]$
表示以第
$i$
个点为根,最右链长度为
$j$
的方案数,然后每次可以把另外一棵子树接在这个后面,具体做法是每次求一遍后缀和,然后把另一棵树接在后面,转移类似一个背包,因此由树形
$DP$
的时间复杂度证明是
$O(n^2)$
的。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 7010
int r[MAXN];
#define P 998244353
int siz[MAXN];
int f[MAXN][MAXN];
int g[MAXN];
void dp(int k)
{
	siz[k] = 1;
	f[k][1] = 1;
	if(k == r[k])return;
	for(int i = k + 1;i <= r[k];i = r[i] + 1)
	{
		dp(i);
		for(int j = siz[k] - 1;j >= 1;--j)f[k][j] = (f[k][j] + f[k][j + 1]) % P;
		for(int j = 1;j <= siz[k] + siz[i];++j)g[j] = 0;
		for(int j = siz[k];j >= 1;--j)
			for(int l = siz[i];l >= 1;--l)
				g[j + l] = (g[j + l] + 1ll * f[k][j] * f[i][l] % P) % P;
		siz[k] += siz[i];
		for(int j = 1;j <= siz[k];++j)f[k][j] = g[j];
	}
	return;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i)r[i] = i;
	int a,b;
	for(int i = 1;i <= m;++i){scanf("%d%d",&a,&b);r[a] = max(r[a],b);}
	r[1] = n;
	for(int i = 1;i <= n;++i)for(int j = i + 1;j <= r[i];++j)r[i] = max(r[i],r[j]);
	dp(1);
	int ans = 0;
	for(int i = 1;i <= n;++i)ans = (ans + f[1][i]) % P;
	cout << ans << endl;
	return 0;
}
 In tag:
DP-树形DP
          In tag:
DP-树形DP 
          
        
        Copyright © 2020
        
          wjh15101051
        
      
      ღゝ◡╹)ノ♡
     Date: Wed Mar 13 18:17:02 CST 2019
          Date: Wed Mar 13 18:17:02 CST 2019
           In Category:
          In Category:
           
         Timeline
            Timeline
           About
            About
           Toolbox
            Toolbox
           Friends
              Friends