JZOJ4800. 周末晚会

来源:互联网 发布:小白源码 编辑:程序博客网 时间:2024/05/16 09:16

题目大意

n个人坐成一桌,有一些是男生,一些是女生。要求不能有超过k个女生连续坐在一起。求所有可能的方案数,循环同构被认为是同一种方案。

Data Constraint
n,k2000

题解

先简单提一下Burnside引理。

Burnside引理

Ans=di|G|

Ans是最后的方案数;di是第i种置换下的不动点数;|G|是置换群的大小,在本题中就是n,因为循环同构只有n种移动方式。

所以,我们只要求出了每种置换下的不动点数,就求出了答案。
考虑当前这种置换是移动i(i[0,n1])个,那么当前置换下就有d=gcd(i,n)个块。我们只要求出了前d个的方案,然后对应地copy过去就能得到一个在当前置换下不变的方案。
现在问题变成求长度为d的合法方案数。由于要copy,所以我们要求这d个的头尾0的数量小于等于k。我们设f[i]表示长度为i+1的串,强制头尾都是1的合法方案数,所以最后方案就是(di)f[i],因为一共有di1个0,然后我们枚举这段插到哪一个空里面,一共di个空位,所以乘上di。维护两个前缀和就能解决了。

时间复杂度:O(n)

SRC

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std ;#define N 2000 + 10typedef long long ll ;const int MO = 1e8 + 7 ;ll f[N] , S[N] , Si[N] ;int T , n , K ;int ans ;int gcd( int x , int y ) { return !y ? x : gcd( y , x % y ) ; }ll Power( ll x , int k ) {    ll s = 1 ;    while ( k ) {        if ( k & 1 ) s = (ll)s * x % MO ;        x = (ll)x * x % MO ;        k /= 2 ;    }    return s ;}int main() {    scanf( "%d" , &T ) ;    while ( T -- ) {        scanf( "%d%d" , &n , &K ) ;        Si[0] = ans = 0 ;        f[0] = S[0] = 1 ;        for (int i = 1 ; i <= n ; i ++ ) {            if ( i - 2 >= K ) f[i] = (S[i-1] - S[i-K-2] + MO) % MO ;            else f[i] = S[i-1] ;            S[i] = (S[i-1] + f[i]) % MO ;            Si[i] = (Si[i-1] + f[i] * i) % MO ;        }        for (int i = 1 ; i <= n ; i ++ ) {            ll d = gcd( i , n ) ;            if ( d - 2 >= K ) ans = ((ans + d * (S[d-1] - S[d-K-2] + MO) % MO) % MO - (Si[d-1] - Si[d-K-2] + MO) % MO + MO) % MO ;            else ans = (ans + d * S[d-1] % MO - Si[d-1] + MO) % MO ;            if ( K >= n ) ans = (ans + 1) % MO ;        }        ans = (ll)ans * Power( n , MO - 2 ) % MO ;        printf( "%d\n" , ans ) ;    }    return 0 ;}

以上.

1 0
原创粉丝点击