jzoj4800. 【GDOI2017模拟9.24】周末晚会

Description

Irena和Sirup正准备下个周末的Party。为这个Party,他们刚刚买了一个非常大的圆桌。他们想邀请每个人,但他们现在不知道如何分配座次。Irena说当有超过K个女孩座位相邻(即这些女孩的座位是连续的,中间没有男孩)的话,她们就会说一整晚的话而不和其他人聊天。
Sirup没有其他选择,只有同意她。然而,作为一名数学家,他很快地痴迷于所有可能方案。
题目说明:
N个人围绕着圆桌坐着,其中一些是男孩,另一些是女孩。你的任务是找出所有合法的方案数,使得不超过K个女孩座位是连续的。
循环同构会被认为是同一种方案。

Input

第一行有一个数T,表示以下有T组数据,每组数据有两个整数N,K。
每组数据之间有一个空行隔开。

Output

输出T行,每行顺次对应一组测试数据。
每组数据你需要输出最后的方案数除以100000007的余数。

Sample Input

3
3 1
3 3
4 1

Sample Output

2
4
3
解释:
第一组数据的方案是:MMM,MMW (M是男孩, W是女孩)。
第二组数据的方案是:MMM,MMW,MWW,WWW。
第三组数据的方案是:MMMM, MMMW,MWMW。

Data Constraint

对于20%的数据T<=20;
对于100%的数据T<=50;
对于20%的数据N,K<=20;
对于100%的数据N,K<=2000;

题解

这题我们利用Burnside引理来做。
不会的戳这里:https://blog.csdn.net/HiChocolate/article/details/91830427

首先我们观察burnside的公式,对于某个置换所得到的循环节长度我们可以知道是gcd(i,n)gcd(i,n)
并且,这些循环节是可以通过旋转得到其他循环节的。
所以说,我们只需要确定gcd(i,n)gcd(i,n)内的不定点个数即可套burnside

如果n<=k的时候,可以直接套burnside,不定点个数即为2gcd(i,n)2^{gcd(i,n)}

如果n>k的时候,就有点麻烦。
我们发现,只需要讨论gcd(i,n)gcd(i,n)这以内的方案。
所以考虑DP。我们设f[i]f_{[i]}表示当前长度为ii,首尾都是男生时并且内部女生数量不过k的方案数。
很好转移,直接枚举倒数第二个男生的位置,累加入f即可。
初始化f[1]=f[2]=1f_{[1]}=f_{[2]}=1
现在我们可以愉快转移了。
首先1~n地去枚举i,得到一个循环节长度。
然后枚举j的长度,因为我们要使得女生个数不过k,所以j从gcd(n,i)kgcd(n,i)-k开始,得到f[j]f_{[j]}贡献即为f[j](gcd(n,i)k+1)f_{[j]}*(gcd(n,i)-k+1)
至于为什么,我们可以发现,f只确定了长度,没有确定位置,所以我们可以同样地去旋转它。
每次旋转在整个循环节旋转i次都是一个全新的不定点。
最后套上burnside即可

标程

uses math;
var
        i,j,k,l,n,m,t:longint;
        g,f:array[-1..2000] of int64;
        mo:int64=100000007;
        ans:int64;
function gcd(a,b:longint):longint;
begin
        if b=0 then exit(a);
        exit(gcd(b,a mod b));
end;
function qsm(a,b:int64):int64;
var
        t,y:int64;
begin
        t:=1;y:=a;
        while b>0 do
        begin
                if b mod 2=1 then t:=t*y mod mo;
                y:=y*y mod mo;
                b:=b div 2;
        end;
        exit(t);
end;

begin
        readln(t);
        while t>0 do
        begin
                dec(t);
                readln(n,k);
                if n<=k then
                begin
                        ans:=0;
                        for i:=1 to n do
                        begin
                                ans:=(ans+qsm(2,gcd(n,i))) mod mo;
                        end;
                        ans:=ans*qsm(n,mo-2) mod mo;
                        writeln(ans);
                        continue;
                end;
                fillchar(f,sizeof(f),0);
                f[1]:=1;f[2]:=1;
                for i:=3 to n do
                begin
                        for j:=max(1,i-k-1) to i-1 do
                        begin
                                f[i]:=(f[i]+f[j]) mod mo;
                        end;
                end;
                ans:=0;
                for i:=1 to n do
                begin
                        for j:=max(gcd(n,i)-k,1) to gcd(n,i) do
                        begin
                                ans:=(ans+f[j]*(gcd(n,i)-j+1)) mod mo;
                        end;
                end;
                ans:=ans*qsm(n,mo-2) mod mo;
                writeln(ans);
        end;
end.
end.

原文地址:https://www.cnblogs.com/RainbowCrown/p/11148355.html