Logo zrl123456 的博客

博客

[ABC217F] Make Pair 讲解

...
zrl123456
2025-12-01 12:51:25
我咋啥也不会/ll

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-03-05 21:11:23

题目
考察:区间 dp,组合数。

状态与边界条件:

我们设 $[l,r]$ 区间的方案数用 $f_{l,r}$ 表示($1\le l,r\le 2n$),那么边界条件就是一个空序列的方案数(即 $f_{i+1,i}$)的方案数便是 $1$。

转移方程:

对于每一个 $f_{l,r}$,若 $l$ 与 $r$ 为朋友关系(我们记作 $vis_{l,r}=1$),那我们就需要先加上 $f_{l+1,r-1}$。
接下来我们需要找到一个点(同学)$k$,把区间 $[l,r]$ 分成两部分,即区间 $[l,k-1]$ 和 $[k,r]$,然后再将区间 $[k,r]$ 分成 $k$ 点,$[k+1,r-1]$ 区间和 $r$ 点,若 $vis_{k,r}=1$,则我们需要加上一个数。
就是下面这张图(paint 画图有点丑):

若只是将 $f_{l,k-1}$ 与 $f_{k,r}$ 相乘,则无法满足题目中对于两种选人的方案,即使同桌关系相同,只要离开队列的顺序不同,也算是不同的方案的条件,那么我们来分析一下:
对于整个 $[l,r]$ 区间需要选 $\frac{r-l+1}{2}$ 次,$[k,r]$ 区间需要选 $\frac{r-k+1}{2}$,那么我们需要在 $\frac{r-l+1}{2}$ 次中选 $\frac{r-k+1}{2}$ 次,这就是组合数,也就是说这一次的答案是 $f_{l,k-1}f_{k+1,r-1}C_{\frac{r-k+1}{2}}^{\frac{r-l+1}{2}}$,我们需要提前维护好任意的 $C_{i,j}$($0\le j\le i\le 2n$)。
那么我们得到最后的转移方程:
$$f_{l,r}=vis_{l,r}f_{l+1,r-1}+\sum_{k=l+2}^{r-1}vis_{k,r}f_{l,k-1}f_{k+1,r-1}C_{\frac{r-k+1}{2}}^{\frac{r-l+1}{2}}$$ 当然,我们得保证 $r-l+1$,$r-k+1$,$k-l$ 均为偶数。
代码:

#include<bits\/stdc++.h>
#define int long long
#define inl inline
#define reg register
#define INF 214748364721474836
using namespace std;
inl int read(){
	int f=1,x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f*x;
}
inl void write(int x){
	if(x<0){
		x=-x;
		putchar('-');
	}
	if(x>=10) write(x\/10);
	putchar(x%10^48);
}
#define MOD 998244353
#define N 405
int n,m,u,v,f[N][N],c[N][N];
bool vis[N][N];
signed main(){
	n=read()<<1;
	m=read();
	for(int i=1;i<=m;++i){
		u=read();
		v=read();
		if((u^v)&1) vis[u][v]=vis[v][u]=true;
	}
	for(int i=0;i<=n;++i) f[i+1][i]=1;
	for(int i=0;i<=n;++i)
		for(int j=0;j<=i;++j) 
			if(j==0) c[i][j]=1;
			else c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
	for(int len=2;len<=n;len+=2)
		for(int l=1;l+len-1<=n;++l){
			int r=l+len-1;
			if(vis[l][r]) f[l][r]=f[l+1][r-1];
			for(int k=l+2;k<r;k+=2)
				if(vis[k][r]) f[l][r]=(f[l][r]+(f[l][k-1]*f[k+1][r-1]%MOD)*c[len>>1][(r-k+1)>>1]%MOD)%MOD;
		}
	write(f[1][n]%MOD);
	return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。