• 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.
    
    
  • 相关阅读:
    LeetCode 654. 最大二叉树
    LeetCode 617. 合并二叉树
    LeetCode 234. 回文链表
    LeetCode 328. 奇偶链表
    LeetCode 24. 两两交换链表中的节点
    LeetCode 21. 合并两个有序链表
    LeetCode 876. 链表的中间结点
    顺序表的定义及其相关基本操作
    LeetCode 206. 反转链表
    LeetCode 111. 二叉树的最小深度
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11148355.html
Copyright © 2020-2023  润新知