首页 > 其他分享 >【题解】[HEOI2013]SAO

【题解】[HEOI2013]SAO

时间:2023-03-30 09:24:59浏览次数:44  
标签:sz SAO nxt int 题解 拓扑 HEOI2013 now dp

题目分析:

考虑这是一个树形图,所以就先直接当作树来做。
这个题其实就是让我们求解有多少种拓扑序而且题目中边方向的限制其实就是在限制拓扑序的前后,而一般这种题在设计 \(dp\) 状态时都会考虑将拓扑序放到状态里,因为如果不这样干拓扑序就很难限制。
也就是设 \(dp[i][j]\) 表示以 \(i\) 为根的子树,\(i\) 在拓扑序中排第 \(j\) 位的方案数,那么转移就显然是合并子树,也就是:

\[dp[u][j] \times dp[v][k] \times C \to dp[u][i] \]

这个转移显然需要乘以一个 \(C\),那么下面就是找这个 \(C\) 到底是什么。
考虑这个合并本质上就是两个序列的合并,因为 \(u\) 子树内访问过的点和 \(v\) 子树的拓扑序分别构成两个序列,现在就是要糅合到一起。
其实也是非常简单的,就是考虑 \(u\) 前面放多少个以及 \(u\) 后面放多少个就好了,即:

\[C = \binom{i-1}{i-j} \times \binom{sz_u + sz_v - i}{sz_v - (i - j)} \]

这样就可以实现 \(O(n^3)\) 的转移。
但是我们也可以发现一点,在转移方程中 \(dp[v][k]\) 中所要枚举的 \(k\) 与其他的项毫无关系,所以就可以考虑直接做一个前缀和,这样就可以不用枚举 \(k\) 了。

上文这种 \(dp\) 的主要原因就是为了方便我们加入边方向的限制,其实边方向的限制就是限制 \(u,v\) 在拓扑序中谁在前谁在后。
若 \(u\) 在 \(v\) 之前:\(i-j < k \to i - j \le k - 1 \to i - j + 1 \le k\)
若 \(u\) 在 \(v\) 之后:\(i-j \ge k\)
因为我们要在 \(u\) 之前插入 \(i-j\) 个来自 \(v\) 子树中的数,而 \(v\) 排在第 \(k\) 个,所以限制一下 \(i-j\) 和 \(k\) 的大小关系就知道 \(v\) 有没有被插入到 \(u\) 之前。
其实就是限制一下 \(k\) 就可以限制拓扑序的前后,就很简单了。
我们就不妨指定 \(1\) 为根,然后去转移,答案即 \(\sum_{i=1}^n dp[1][i]\)

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2005;
const int MOD = 1e9+7;
struct edge{
	int nxt,to;
	edge(){}
	edge(int _nxt,int _to){
		nxt = _nxt,to = _to;
	}
}e[2 * N];
int head1[N],head2[N],C[N][N],cnt,f[N][N],sz[N],g[N];
void add_edge(int *head,int from,int to){
	e[++cnt] = edge(head[from],to);
	head[from] = cnt;
}
int binom(int n,int m){
	if(n < 0 || m < 0 || n < m)	return 0;
	return C[n][m];	
}
void dfs(int now,int fath){
	memset(f[now]+1,0,sizeof(int)*sz[now]);
	sz[now] = 1,f[now][1] = 1;
	for(int i=head1[now]; i;i=e[i].nxt){  //要求 now 在 to 之前 
		int to = e[i].to;
		if(to == fath)	continue;
		dfs(to,now);memcpy(g+1,f[now]+1,sizeof(int)*sz[now]);memset(f[now]+1,0,sizeof(int)*sz[now]);
		for(int j=1; j<=sz[now]; j++){
			for(int i=j; i<=sz[to]+j-1; i++){
				f[now][i] = (f[now][i] + g[j]*binom(i-1,i-j)%MOD*binom(sz[now]+sz[to]-i,sz[to]-(i-j))%MOD*(f[to][sz[to]]-f[to][i - j] + MOD)%MOD)%MOD;
			}
		}
		sz[now] += sz[to];
	}
	for(int i=head2[now]; i;i=e[i].nxt){
		int to = e[i].to;
		if(to == fath)	continue;
		dfs(to,now);memcpy(g+1,f[now]+1,sizeof(int)*sz[now]);memset(f[now]+1,0,sizeof(int)*sz[now]);
		for(int j=1; j<=sz[now]; j++){
			for(int i=j+1; i<=sz[to]+j; i++){
				f[now][i] = (f[now][i] + g[j]*binom(i-1,i-j)%MOD*binom(sz[now]+sz[to]-i,sz[to]-(i-j))%MOD*f[to][i-j]%MOD)%MOD;
			}
		}
		sz[now] += sz[to];
	}
//	for(int i=1; i<=sz[now]; i++)	printf("f[%lld][%lld] = %lld\n",now,i,f[now][i]);
	for(int i=1; i<=sz[now]; i++)	f[now][i] = (f[now][i] + f[now][i-1])%MOD;
}
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		int n;scanf("%lld",&n);
		for(int i=1; i<n; i++){
			int a,b;string s;
			cin>>a>>s>>b;a++,b++;
			if(s == "<")	add_edge(head1,a,b),add_edge(head2,b,a);
			else	add_edge(head1,b,a),add_edge(head2,a,b);
		}
		C[0][0] = 1;
		for(int i=1; i<=n; i++){
			C[i][0] = 1;
			for(int j=1; j<=n; j++){
				C[i][j] = (C[i-1][j] + C[i-1][j-1])%MOD;
			}
		}
		dfs(1,0);
		int ans = f[1][sz[1]];
		printf("%lld\n",ans);
		for(int i=1; i<=n; i++)	head1[i] = head2[i] = 0;
		cnt = 0;
	}
	return 0;
}

标签:sz,SAO,nxt,int,题解,拓扑,HEOI2013,now,dp
From: https://www.cnblogs.com/linyihdfj/p/17271246.html

相关文章

  • 【题解】Codeforces Round 861(CF1808)A - E1
    我忘记了今天有阳间CF,所以就开打的很晚,所以只是说一下做法,代码实现....还是算了吧。但是我也看了,我的思路其他的人都有写,所以这个做法正确性没问题。A.LuckyNumbers题......
  • CF1009F 题解
    一、题目描述:给定一棵以 1 为根,n 个节点的树。设 d(u,x) 为 u的子树中到 u 距离为 x 的节点数。对于每个点,求一个最小的 k,使得 d(u,k) 最大。 二、......
  • CentOS7中远程连接数据库连不上的问题解决方法
      当远程连接数据库连接不起来时:可能原因:1.检查网络防火墙或其他安全设置是否阻止了连接  2.mysql服务是否启动,查看systemctlstatusmysql3.是否提前授权:......
  • ABC291题解(D-G)
    ABC291D-FlipCardsSolution:考虑DP,定义状态\(F_{i,0}\)为第\(i\)张卡片正面朝上的方案数,\(F_{i,1}\)为第\(i\)张卡片背面朝上的方案数,每次check是否相同然后转移即可......
  • 使用SQLALCHEMY 出现warning的问题解决
    运行程序时出现错误:UserWarning:NeitherSQLALCHEMY_DATABASE_URInorSQLALCHEMY_BINDSisset.DefaultingSQLALCHEMY_DATABASE_URIto"sqlite:///:memory:".'Neithe......
  • CF429D Tricky Function 题解 分治/平面最近点对
    题目链接:http://codeforces.com/problemset/problem/429/D题目大意:给定一个长度为\(n\)的数列\(a_1,a_2,\ldots,a_n\)。用\(s\)表示\(a\)的前缀和数组,即\(s_......
  • buu [CISCN] BadProgrammer题解
    [CISCN]BadProgrammer页面很长,有很多的按钮,但是点了之后都没反应查看源码、扫描打开到具体目录一个个目录点开看,在static/下找到了一个flag.ejs文件下载,打开......
  • [ARC131D] AtArcher 题解
    题意数轴上有一个箭靶以\(0\)为轴心左右对称,给定每个得分区域的范围和分值,要求射\(N\)支箭在靶上,且任意两支箭的距离不少于\(D\),求最大得分。保证从中心向两侧分数不......
  • android:state_pressed标签失效或android:state_enabled标签失效问题解决
    问题描述:android:state_pressed标签失效或android:state_enabled标签失效,点击不会变色,可用/不可用时不会变色。 <?xmlversion="1.0"encoding="utf-8"?><selector......
  • 省选武汉联测 13 题解
    省选模拟赛俩构造一交互挺nm逆天。赛后题解区就一句Surprise!!!没题解也挺nm逆天。那建议组题人的马先消失一下。这时候就体现学长博客的重要性了。搜关键词搜到三......