题目
思路
我是垃圾。不看神的博客我什么也不会做。
我想要确定一个连通块的模样,感觉完全不行。然而事实上,我们可以枚举连通块节点编号的
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+1∑r−1f(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=0n−1(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;
}