【WC2017四校联考5】B君的宴请 题解

题目大意

  n 把椅子排成一个环。
  现在要撤掉一部分,使得只剩下恰好 k 把椅子,并且剩下的任意两把椅子原先不相邻。
  若两种方案可以经过旋转、翻转互相等价,则认为本质相同。
  求本质不同的方案数 $\bmod 10^9+7$

一个比较“正统”的思路

  首先把撤与不撤看作是01染色,0 表示撤,1 表示保留。

  考虑 burnside 引理。
  置换群大小为 2n,包括 n 个旋转置换和 n 个旋转+翻转置换。

  考虑 n 个旋转置换的不动点。
  对于第 i 个旋转置换(0<= i <=n-1),只有前面 d=gcd(i,n) 个元素可自由选择颜色。所以模型变成:长度为 d 的序列,我要分配 k’ 个黑色进去,要求任意两个黑色不相邻,且头尾不能同时为黑,的方案数。
  (这里因为原序列被划分成 n/d 段,所以这里 k’=k/(n/d),如果不能整除则视为该置换下不动点为 0。)
  那么这个模型就是一个组合数模型了。先不考虑头尾的限制。类似于挡板原理,我们把作为间隔的 k’-1 个白点抽出来,则 k’ 个黑点就没有不相邻的限制了,所以方案数就是 $C_{d-(k’-1)}^{k’}$。如果考虑头尾限制,则用总的减去头尾同时为黑的方案数,头尾同时为黑意味着次头和次尾同时为白,所以方案数是 $C_{(d-4)-((k’-2)-1)}^{k’-2}$

  考虑 n 个旋转+翻转置换的不动点。我们知道旋转+翻转置换可以等价为翻转置换。
  n 为奇数时,对称轴穿过一个点和一个空隙,设这个点为 A。由于空隙旁的两个点相邻,所以他们必为白色。若 k 为奇数,则 A 为黑色,A 的下一个点为白色,所以方案数是 $C_{(\lfloor\frac{n}{2}\rfloor-2)-(\lfloor\frac{k}{2}\rfloor-1)}^{\lfloor\frac{k}{2}\rfloor}$。若 k 为偶数,则 A 为白色,方案数类似。
  n 为偶数时,对称轴有两种,各有 n/2 条。一种是穿过两个点的,一种是穿过两个空隙的。分析方法类似,第一种对称轴要讨论 k 的奇偶性,第二种对称轴则要求 k 必须是偶数,否则为0。

一个大家都想到的思路

  这里就直接搬题解了。

  首先考虑 k 个椅子之间的一个置换(而不是像通常那样考虑 n 个椅子的情况)
  对于 k=1 和 k=2,可以直接计算答案。
  对于更大的 k 的情况,同样需要计算旋转和轴对称。
  如果 k 个椅子的位置确定,那么接下来的步骤就是将 n-k 个将要被撤掉的椅子,放进 k 个椅子的间隔中,使得每个间隔中至少有一个椅子。
  旋转的情况比较简单,就是和最大公约数相关。
  如果是轴对称的话,还需要根据 k 的奇偶性进行迚一步的讨论。
  如果 k 是奇数,那么对称之后一定是(k-1)/2 对 和 单独的一个点
  如果 k 是偶数,那么对称后可能是 k/2 对,还可能是 k/2-1 对和 2 个单独的点

代码

//用第一种方法的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxn=1e6+5;
const LL mo=1e9+7;

int n,k;

LL gcd(LL a,LL b) {return (b) ?gcd(b,a%b) :a ;}

LL mi(LL x,LL y)
{
LL re=1;
for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;
return re;
}

LL fac[maxn],ny[maxn];
LL C(LL n,LL m)
{
if (n<0 || m<0) return 0;
return fac[n]*ny[m]%mo*ny[n-m]%mo;
}

int main()
{
scanf("%d %d",&n,&k);
if (n==1 || k==0) {printf("1\n"); return 0;}

fac[0]=ny[0]=1;
fo(i,1,n) fac[i]=fac[i-1]*i%mo, ny[i]=mi(fac[i],mo-2)%mo;

LL ans=0;
fo(i,1,n)
{
LL d=gcd(i,n);
if (k%(n/d)!=0) continue;
int kk=k/(n/d);
if (d==1) continue;
ans=(ans+C(d-(kk-1),kk)-C(d-4-(kk-3),kk-2)+mo)%mo;
}
if (n&1)
{
if (k&1) ans=(ans+C(n/2-2-(k/2-1),k/2)*n%mo)%mo;
else ans=(ans+C(n/2-1-(k/2-1),k/2)*n%mo)%mo;
} else
{
if (k%2==0) ans=(ans+C(n/2-2-(k/2-1),k/2)*(n/2)%mo)%mo;

if (k&1)
{
ans=(ans+C(n/2-2-(k/2-1),k/2)*n%mo)%mo;
} else
{
ans=(ans+(C(n/2-1-(k/2-1),k/2)+C(n/2-3-((k-2)/2-1),(k-2)/2))%mo*(n/2)%mo)%mo;
}
}
ans=ans*mi(n*2,mo-2)%mo;

printf("%lld\n",ans);
}