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的公式,对于某个置换所得到的循环节长度我们可以知道是 g c d ( i , n ) gcd(i,n) gcd(i,n)
并且,这些循环节是可以通过旋转得到其他循环节的。
所以说,我们只需要确定 g c d ( i , n ) gcd(i,n) gcd(i,n)内的不定点个数即可套burnside

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

如果n>k的时候,就有点麻烦。
我们发现,只需要讨论 g c d ( i , n ) gcd(i,n) gcd(i,n)这以内的方案。
所以考虑DP。我们设 f [ i ] f_{[i]} f[i]表示当前长度为 i i i,首尾都是男生时并且内部女生数量不过k的方案数。
很好转移,直接枚举倒数第二个男生的位置,累加入f即可。
初始化 f [ 1 ] = f [ 2 ] = 1 f_{[1]}=f_{[2]}=1 f[1]=f[2]=1
现在我们可以愉快转移了。
首先1~n地去枚举i,得到一个循环节长度。
然后枚举j的长度,因为我们要使得女生个数不过k,所以j从 g c d ( n , i ) − k gcd(n,i)-k gcd(n,i)k开始,得到 f [ j ] f_{[j]} f[j]贡献即为 f [ j ] ∗ ( g c d ( n , i ) − k + 1 ) 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.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值