• bzoj2281


    有思维难度的好题
    这种题我们一般可以先从部分分考虑
    30%的分数k=2也就是黑白各一个
    不难发现如果初始黑白棋子相邻那必然是先手必败态否则一定是先手必胜
    那么30分的部分分是很容易拿到的,组合数学
    如果有多个棋子,必败态又是什么呢
    由于黑白棋是相间隔的,我们不妨依次编号1号白棋,1号黑棋……一直编到k/2,我们记作m
    同号棋子都相邻则一定是必败态,这个很容易看出来,还有呢?
    我们假设编号为i的黑白棋之间有d[i]个格子
    我们知道白棋往左移是没有意义的,因为黑棋也可以按同样的方向移同样多的格子
    这样同号棋子间的间隔不变,本质还是刚才的状态,同理,黑棋往右移也是没有意义的
    现在也就是每人轮流可以把d个白棋(黑棋)往右(左)移若干个格子
    若是最后移动成同号棋子都相邻的那个人获胜
    也就是,一共有m堆石子,每个人可以从最多d堆取任意多个,最后取完的获胜!
    这不就是nim取石子的扩展吗?
    考虑经典的nim取石子问题,在任意一堆中取任意个
    若n堆石子的异或和=0则为先手必败
    n堆石子异或和=0即二进制每一位异或和为0,也就是每一位和mod 2=0
    于是我们脑洞大开的猜想,如果可以在1~d堆中任意取,
    那先手必败态是否是每一位和mod (d+1)=0?
    怎么证?其实很简单,首先全是0必然必败不多说
    由于是可以取任意多个,所以二进制位之间互不影响
    如果存在某位和x是(d+1)的倍数且不为0,那么先手操作最多只能对x-(0~d)因为最多操作d堆
    那么下面,我仍然可以把x还原成(d+1)的倍数
    所以很明显每一位和都mod (d+1)=0是先手必败态,否则为必胜
    下面就是统计,我们显然可以用补集的思想,统计先手必败的数目
    显然∑d[i]也就是m堆石子总和是在[0,n-k]之间
    我们设f[i,j]表示到二进制第i位石子堆和为j的方案数,然后穷举这位和是(d+1)的倍数的情况
    然后组合数学搞一搞,由于组合涉及到除法取模,所以我们还要求一下乘法逆元

     1 const mo=1000000007;
     2 var f:array[-1..15,0..10010] of int64;
     3     a,b:array[0..10010] of int64;
     4     d,t,n,k,m,i,j,p,q:longint;
     5     ans:int64;
     6 
     7 function min(a,b:longint):longint;
     8   begin
     9     if a>b then exit(b) else exit(a);
    10   end;
    11 
    12 function quick(y:int64;n:longint):int64;
    13   begin
    14     quick:=1;
    15     while n>0 do
    16     begin
    17       if n mod 2=1 then quick:=quick*y mod mo;
    18       n:=n shr 1;
    19       y:=y*y mod mo;
    20     end;
    21   end;
    22 
    23 function c(n,m:longint):int64;
    24   begin
    25     exit(a[n]*b[m] mod mo*b[n-m] mod mo);
    26   end;
    27 
    28 begin
    29   readln(n,k,d);
    30   m:=k div 2;
    31   a[0]:=1;
    32   for i:=1 to n do
    33     a[i]:=a[i-1]*int64(i) mod mo;
    34   b[0]:=1;
    35   for i:=1 to n do
    36     b[i]:=quick(a[i],mo-2); //a^(p-1)≡1 (mod p) 明显a的乘法逆元即a^(p-2)
    37   f[-1,0]:=1;
    38   t:=trunc(ln(n)/ln(2));
    39   for i:=0 to t do
    40     for p:=0 to min(n-k,m*(1 shl i-1)) do
    41       for j:=0 to min(m div (d+1),(n-k-p) div (1 shl i) div (d+1)) do
    42       begin
    43         q:=p+j*(d+1)*1 shl i;
    44         f[i,q]:=(f[i,q]+f[i-1,p]*c(m,j*(d+1)) mod mo) mod mo;
    45       end;
    46 
    47   for i:=0 to n-k do
    48     ans:=(ans+f[t,i]*c(n-i-k+m,m) mod mo) mod mo;//剩下的格子在邻号棋子之间放置
    49   writeln((c(n,k)-ans+mo) mod mo);
    50 end.
    View Code
  • 相关阅读:
    mdx 根据维度Hierarchy节点的名字来filter节点,搜索节点
    学习C++.Primer.Plus 8 函数探幽
    学习C++.Primer.Plus 7 函数
    学习C++.Primer.Plus 6 分支语句和逻辑操作符
    学习C++.Primer.Plus 5 循环和关系表达式
    学习C++.Primer.Plus 4 复合类型
    NYoj_171聪明的kk
    NYoj_104最大和
    希尔排序
    NYoj_49开心的小明
  • 原文地址:https://www.cnblogs.com/phile/p/4472979.html
Copyright © 2020-2023  润新知