AGC028 D Chords dp

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/83109712

题目链接
题意:
在一个环上有2n个点,按顺时针编号,你需要将这些点两两配对相连,形成若干个连通块。连通的含义是只要连接的两个点能通过它配对点的直线,经过与其他线段的交点能到达另一条线。现在已经有k对点配对好了,告诉你这k对点的编号,问你剩下的n-k对的所有配对方法最后形成的连通块数量之和。对1e9+7取模。

题解:
我还是水平好菜的,遇到一些有难度的计数题还是毫无思路啊。

这题考虑dp,我们设dp[i][j]为i与j连通,并且连通块内最小编号是i,最大编号是j的方案数,那么它对答案的贡献就是 d p [ i ] [ j ] dp[i][j]* 其他没连的随便连的方案数。我们先 n 2 n^2 统计出 i i j j 的中间有多少个点没有连过,记为 c n t [ [ i ] [ j ] cnt[[i][j] ,然后考虑如何计算dp数组。我们发现直接算并不好算,我们可以用 i i j j 中的数随便连的方案数减去其中没有让 i i j j 连通的方案数。我们预处理出 i i 个点随便连的方案数 g [ i ] g[i] ,对于 i i 是奇数,答案是0,对于偶数 x x ,答案是 i = 1 x 1 i \prod_{i=1}^{x-1}i 。那么对于 i i j j 之间没有连到 i i j j 以外的边的情况(连出去最小数和最大数就不是 i i j j 了),我们有 d p [ i ] [ j ] = g [ i ] k = i + 1 j 1 d p [ i ] [ k ] g [ c n t [ i ] [ j ] ] dp[i][j]=g[i]-\sum_{k=i+1}^{j-1}dp[i][k]*g[cnt[i][j]] 。dp方程的含义是枚举 i i 没有与 j j 连通,那么与它连通的最大数是 j j ,后面的那些点随便连,减去这些情况的方案数。这样当前问题就可以由之前的子问题计算得来了。
最后统计一下答案即可。

代码:

#include <bits/stdc++.h>
using namespace std;

const long long mod=1e9+7;
int n,k,a[610],b[610],pd[610];
long long cnt[610][610],dp[610][610],g[2000],ans;
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;++i)
	{
		scanf("%d%d",&a[i],&b[i]);
		pd[a[i]]=b[i];
		pd[b[i]]=a[i];
	}
	n<<=1;
	g[0]=1;
	for(int i=2;i<=n;i+=2)
	g[i]=(g[i-2]*(i-1))%mod;
	for(int i=1;i<=n;++i)
	{
		for(int j=i;j<=n;++j)
		cnt[i][j]=cnt[i][j-1]+(!pd[j]);
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=i;j<=n;++j)
		{
			int ji=0;
			for(int k=i;k<=j;++k)
			{
				if(pd[k]&&(pd[k]>j||pd[k]<i))
				ji=1;				
			}
			if(ji==1)
			continue;
			dp[i][j]=g[cnt[i][j]];
			for(int k=i+1;k<=j-1;++k)
			dp[i][j]=(dp[i][j]-1ll*dp[i][k]*g[cnt[k+1][j]]%mod+mod)%mod;
		}
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=i;j<=n;++j)
		ans=(ans+dp[i][j]*g[(n/2-k)*2-cnt[i][j]]%mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/83109712