JZOJ 7066. 【2021.4.24 NOI模拟】ehzeux与圆周(DP)

JZOJ 7066. 【2021.4.24 NOI模拟】ehzeux与圆周

题目大意

  • 圆周上有 2 ∗ n 2*n 2n个点,两两相连构成 n n n个点对,其中有 m m m个点对已经连好,求所有方案下的连通块数量和。当两个点相连或所在线段相交则称之为属于同一个连通块。
  • n , m ≤ 300 n,m\le300 n,m300

题解

  • 需要发现一个性质,把圆周展开成一条线段后和原来是一样的,并不会影响线段的连通。
  • 考虑把每种情况的连通块数量总和,转化为每个连通块出现的次数总和。
  • f i , j f_{i,j} fi,j [ i , j ] [i,j] [i,j]为一个连通块时区间内不同连边的方案数,且 [ i , j ] [i,j] [i,j]外连边的点有 x x x个,则对答案的总贡献为 f i , j ∗ x ! 2 i 2 ( i / 2 ) ! f_{i,j}*\frac{x!}{2^{\frac{i}{2}}(i/2)!} fi,j22i(i/2)!x!,设后面的系数为 g x g_x gx,含义为把所有数全排列后相邻的两两连边,去掉每对顺序可以交换,且不同对之间顺序可以打乱的方案数。
  • 转移只需用一个区间内各种连边的总数减去不合法的方案数,即用当前区间内各种连边总方案减去 [ i , l ] ( l ∈ ( i , j ) ) [i,l](l\in(i,j)) [i,l](l(i,j))作为一个完整连通块的方案数。
  • 记得若 [ i , j ] [i,j] [i,j]中有向外连的边,则 f i , j = 0 f_{i,j}=0 fi,j=0

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 310
#define ll long long
#define md 1000000007
struct {
	int x, y;
}a[N];
ll f[N * 2][N * 2], g[N * 2];
int si[N * 2], p[N * 2][N * 2];
int main() {
	int n, m, i, j, k, l;
	ll ans = 0;
	scanf("%d%d", &n, &m);
	n *= 2;
	for(i = 1; i <= m; i++) {
		scanf("%d%d", &a[i].x, &a[i].y);
		si[a[i].x] = si[a[i].y] = 1;
		p[a[i].x][a[i].y] = p[a[i].y][a[i].x] = 1;
	}
	for(i = 1; i <= n; i++) si[i] = si[i - 1] + (si[i] == 0);
	
	for(i = 0; i <= n; i++) if(i % 2 == 0) {
		g[i] = 1;
		int t = i / 2;
		for(j = i / 2 + 1; j <= i; j++) {
			g[i] = g[i] * j;
			while(t && g[i] % 2 == 0) g[i] /= 2, t--;
			g[i] %= md;
		}
	}
	
	for(k = 1; k <= n; k++) if(k % 2 == 0) {
		for(i = 1; i + k - 1 <= n; i++) {
			j = i + k - 1;
			f[i][j] = g[si[j] - si[i - 1]];
			for(l = i + 1; l < j; l++) {
				f[i][j] = (f[i][j] - f[i][l] * g[si[j] - si[l]] % md + md) % md;
			}
			
			for(l = 1; l <= m && f[i][j]; l++) {
				if((a[l].x >= i && a[l].x <= j) != (a[l].y >= i && a[l].y <= j)) f[i][j] = 0;
			}
			(ans += f[i][j] * g[si[i - 1] + si[n] - si[j]] % md) %= md;
		}
	}
	
	printf("%lld\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值