[AGC028D]Chords

220 篇文章 2 订阅
14 篇文章 0 订阅

题目

传送门 to AtCoder

思路

我是垃圾。不看神的博客我什么也不会做。

在这里插入图片描述

我想要确定一个连通块的模样,感觉完全不行。然而事实上,我们可以枚举连通块节点编号的 min ⁡ \min min max ⁡ \max max,分别设为 l , r l,r l,r 。显然外面的点会被隔开,因此可以记 f ( l , r ) f(l,r) f(l,r) 为给 [ l , r ] [l,r] [l,r] 内的点连边使得 l , r l,r l,r 连通。涉及连通性问题时,往往可以先控制为子集,然后减去真子集情况(很多状压 d p \tt dp dp 都这样)。本题中就有
f ( l , r ) = g ( l , r ) − ∑ i = l + 1 r − 1 f ( l , i ) ⋅ g ( i + 1 , r ) f(l,r)=g(l,r)-\sum_{i=l+1}^{r-1}f(l,i)\cdot g(i{+}1,r) f(l,r)=g(l,r)i=l+1r1f(l,i)g(i+1,r)

其中 g g g 是无限制选择的数量。其值是平凡的:只要总点数是 2 n 2n 2n,那么连边方案就是 ∏ i = 0 n − 1 ( 2 i + 1 ) \prod_{i=0}^{n-1}(2i{+}1) i=0n1(2i+1) 。已经连了边的点就直接扔掉。

然后就 O ( n 3 ) \mathcal O(n^3) O(n3) 做完了。

代码

提前判断区间内已固定的边是否有非法的(连向区间外的)比较省事。

#include <cstdio> // megalomaniac JZM yydJUNK!!!
#include <iostream> // Almighty XJX yyds!!!
#include <algorithm> // oracle: ZXY yydBUS!!!
#include <cstring> // DDG yyd Black Bridge!!!
#include <cctype> // decent XYX yydLONELY!!!
typedef long long llong;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a*f;
}

const int MOD = 1e9+7, MAXN = 605;
int to[MAXN], g[MAXN], inner[MAXN][MAXN];
int f[MAXN][MAXN], cnt[MAXN][MAXN];
int main(){
	int n = readint(), k = readint();
	for(int i=1,a,b; i<=k; ++i){
		a = readint(), b = readint();
		to[a] =	b, to[b] = a;
	}
	rep(i,g[0]=1,n) g[i<<1] = int(
		llong((i<<1)-1)*g[(i-1)<<1]%MOD);
	rep(i,1,n<<1) rep(j,i,n<<1){
		cnt[i][j] = cnt[i][j-1], inner[i][j] = inner[i][j-1];
		if(to[j]) ++ cnt[i][j]; else continue;
		if(i <= to[j] && to[j] < j) ++ inner[i][j];
	}
	int ans = 0; rep(i,0,n<<1) f[i+1][i] = 1;
	drep(l,(n<<1)-1,1) for(int r=l+1; r<=(n<<1); r+=2){
		if(cnt[l][r] != (inner[l][r]<<1)) continue;
		int all = r-l+1-cnt[l][r], &w = f[l][r] = g[all];
		for(int i=l+1; i!=r; i+=2) w = int((w+llong(
			MOD-f[l][i])*g[r-i-cnt[i+1][r]])%MOD);
		ans = int((ans+llong(w)*g[((n-k)<<1)-all])%MOD);
	}
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值