圆周上有
2
∗
n
2*n
2∗n个点,两两相连构成
n
n
n个点对,其中有
m
m
m个点对已经连好,求所有方案下的连通块数量和。当两个点相连或所在线段相交则称之为属于同一个连通块。
n
,
m
≤
300
n,m\le300
n,m≤300
题解
需要发现一个性质,把圆周展开成一条线段后和原来是一样的,并不会影响线段的连通。
考虑把每种情况的连通块数量总和,转化为每个连通块出现的次数总和。
设
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,j∗22i(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>usingnamespace std;#define N 310#define ll long long#define md 1000000007struct{int x, y;}a[N];
ll f[N *2][N *2], g[N *2];int si[N *2], p[N *2][N *2];intmain(){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);return0;}