题目

首先明确先手的棋子是往左走的,将其称为棋子1;后手的棋子是往右走的,将其称为棋子2。

如果有一些行满足1在2右边,也就是面对面,那其实就是一个nim,每一行都是一堆石子,数量是两个棋子之间的空格数。这些行称为nim行。

如果一些行1在2左边,那么两个人能走的步数是互不影响的;在这种行里,不管是先手还是后手都只会一次走1格,而且不同的这种行都是等价的,所以我们可以把所有1在2左边的行中,每个人能走的步数都分别求和,称为这个人的"额外步数"。

把所有nim行构成的nim游戏,和额外步数分成两个部分。观察发现会有这些结论:

1.先手nim游戏胜利,额外步数比后手多。显然先手会赢。

2.先手nim游戏失败,额外步数多。第一步,先手可以选择先动nim游戏,如果nim石子个数本来就全0则用一个额外步数。接下来,后手动nim我就动nim,后手动额外我就动额外。最终nim游戏我会输掉,但是这时额外步数仍然是先手多,接下来先手再动额外就可以让后手输掉。先手赢。

3.先手nim游戏胜利,额外步数比后手少。第一步如果动nim,则对后手来说变成第2种情况;动额外,则对后手来说变成第1种情况。先手必输。

4.先手nim游戏失败,额外步数少。显然会输。

5.额外步数两者相等,很容易看出赢了nim的人最终会赢,证明和上面几条类似(讨论先手第一步动nim还是动额外)。

所以可以看出,判断输赢时先比较额外步数,如果相同再比较nim输赢。


令\(dp_{i,j,k}\)表示已经填了i行,先手额外步数-后手额外步数=j,前i行所有nim行的异或和是k的方案数。把\(dp_i\)看成一个矩阵。转移的时候枚举每一行的状态,发现转移其实是一个二维卷积的形式(第一维正常卷积,第二维XOR卷积)。并且一共h行,每行的转移都是卷上了一个相同的矩阵,所以其实这个dp的本质是二维多项式快速幂。(二维FFT)还是按照正常二维FFT的方式来写,把列的DFT和IDFT改成FWT和IFWT就好了。这里快速幂的幂次比较小,所以不用exp什么的,可以在DFT和FWT做完之后把每个点值都取h次幂,然后做IFWT和IDFT。

在上面写出的dp状态中,j的值域是\([-2(w-2)h,2(w-2)h]\),k的值域是\([0,2^{\lceil log_2w \rceil}]\),所以总复杂度\(O(hw^2log(hw))\)。

点击查看代码
  1. #include <bits/stdc++.h>
  2. #define rep(i,n) for(int i=0;i<n;++i)
  3. #define repn(i,n) for(int i=1;i<=n;++i)
  4. #define LL long long
  5. #define pii pair <int,int>
  6. #define pb push_back
  7. #define fi first
  8. #define se second
  9. #define mpr make_pair
  10. using namespace std;
  11. const LL MOD=998244353;
  12. LL qpow(LL x,LL a)
  13. {
  14. LL res=x,ret=1;
  15. while(a>0)
  16. {
  17. if((a&1)==1) ret=ret*res%MOD;
  18. a>>=1;
  19. res=res*res%MOD;
  20. }
  21. return ret;
  22. }
  23. namespace NTT
  24. {
  25. vector <LL> rev;
  26. void ntt(vector <LL> &a,LL G)
  27. {
  28. LL nn=a.size(),gn,g,x,y;vector <LL> tmp=a;
  29. rep(i,nn) a[i]=tmp[rev[i]];
  30. for(int len=1;len<nn;len<<=1)
  31. {
  32. gn=qpow(G,(MOD-1)/(len<<1));
  33. for(int i=0;i<nn;i+=(len<<1))
  34. {
  35. g=1;
  36. for(int j=i;j<i+len;++j,(g*=gn)%=MOD)
  37. {
  38. x=a[j];y=a[j+len]*g%MOD;
  39. a[j]=(x+y)%MOD;a[j+len]=(x-y+MOD)%MOD;
  40. }
  41. }
  42. }
  43. }
  44. vector <LL> convolution(vector <LL> a,vector <LL> b,LL G)
  45. {
  46. LL nn=1,bt=0,sv=a.size()+b.size()-1;while(nn<a.size()+b.size()-1) nn<<=1LL,++bt;
  47. while(a.size()<nn) a.pb(0);while(b.size()<nn) b.pb(0);
  48. rev.clear();
  49. rep(i,nn)
  50. {
  51. rev.pb(0);
  52. rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
  53. }
  54. ntt(a,G);ntt(b,G);
  55. rep(i,nn) (a[i]*=b[i])%=MOD;
  56. ntt(a,qpow(G,MOD-2));
  57. while(a.size()>sv) a.pop_back();
  58. LL inv=qpow(nn,MOD-2);
  59. rep(i,a.size()) (a[i]*=inv)%=MOD;
  60. return a;
  61. }
  62. }
  63. LL h,w,inv2=qpow(2,MOD-2);
  64. vector <vector <LL> > bas,ans;
  65. vector <vector <LL> > mul(vector <vector <LL> > a)//行FFT,列FWT
  66. {
  67. LL nn=a[0].size(),bit=0;while((1LL<<bit)<nn*h) ++bit;
  68. nn=1LL<<bit;
  69. rep(i,32) while(a[i].size()<nn) a[i].pb(0);
  70. NTT::rev.clear();
  71. rep(i,nn)
  72. {
  73. NTT::rev.pb(0);
  74. NTT::rev[i]=(NTT::rev[i>>1]>>1)|((i&1)<<(bit-1));
  75. }
  76. rep(i,32) NTT::ntt(a[i],3);
  77. rep(i,nn)
  78. {
  79. rep(bt,5) rep(msk,32) if((msk&(1<<bt))==0)
  80. {
  81. LL x=a[msk][i],y=a[msk|(1<<bt)][i];
  82. a[msk][i]=(x+y)%MOD;a[msk|(1<<bt)][i]=(x-y+MOD)%MOD;
  83. }
  84. }
  85. rep(i,32) rep(j,nn) a[i][j]=qpow(a[i][j],h);
  86. rep(i,nn)
  87. {
  88. for(int bt=4;bt>=0;--bt) rep(msk,32) if((msk&(1<<bt))==0)
  89. {
  90. LL x=a[msk][i],y=a[msk|(1<<bt)][i];
  91. a[msk][i]=(x+y)*inv2%MOD;a[msk|(1<<bt)][i]=(x-y+MOD)*inv2%MOD;
  92. }
  93. }
  94. rep(i,32) NTT::ntt(a[i],qpow(3,MOD-2));
  95. LL mulv=qpow(nn,MOD-2);
  96. rep(i,32) rep(j,nn) (a[i][j]*=mulv)%=MOD;
  97. return a;
  98. }
  99. int main()
  100. {
  101. cin>>h>>w;
  102. rep(i,32)
  103. {
  104. bas.pb(vector <LL>{});
  105. rep(j,w+w-3) bas[i].pb(0);
  106. }
  107. rep(i,w) rep(j,w) if(i!=j)
  108. {
  109. if(i<j)
  110. {
  111. int ih=i,jh=w-1-j;
  112. ++bas[0][ih-jh+w-2];
  113. }
  114. else
  115. {
  116. int mm=i-j-1;
  117. ++bas[mm][w-2];
  118. }
  119. }
  120. ans=mul(bas);
  121. LL anss=0;
  122. rep(i,32) rep(j,ans[i].size())
  123. {
  124. LL asum=j-(w-2)*h;
  125. if(asum>0) (anss+=ans[i][j])%=MOD;
  126. else if(asum==0) if(i>0) (anss+=ans[i][j])%=MOD;
  127. }
  128. cout<<anss<<endl;
  129. return 0;
  130. }

[题解] Atcoder Beginner Contest ABC 265 Ex No-capture Lance Game DP,二维FFT的更多相关文章

  1. [题解] Atcoder Beginner Contest ABC 270 G Ex 题解

    点我看题 G - Sequence in mod P 稍微观察一下就会发现,进行x次操作后的结果是\(A^xS+(1+\cdots +A^{x-1})B\).如果没有右边那一坨关于B的东西,那我们要求 ...

  2. 题解 AtCoder Beginner Contest 168

    小兔的话 欢迎大家在评论区留言哦~ AtCoder Beginner Contest 168 A - ∴ (Therefore) B - ... (Triple Dots) C - : (Colon) ...

  3. AtCoder Beginner Contest 053 ABCD题

    A - ABC/ARC Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement Smeke has ...

  4. KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200) 题解

    KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200) 题解 哦淦我已经菜到被ABC吊打了. A - Century 首先把当前年 ...

  5. AtCoder Beginner Contest 184 题解

    AtCoder Beginner Contest 184 题解 目录 AtCoder Beginner Contest 184 题解 A - Determinant B - Quizzes C - S ...

  6. AtCoder Beginner Contest 154 题解

    人生第一场 AtCoder,纪念一下 话说年后的 AtCoder 比赛怎么这么少啊(大雾 AtCoder Beginner Contest 154 题解 A - Remaining Balls We ...

  7. AtCoder Beginner Contest 153 题解

    目录 AtCoder Beginner Contest 153 题解 A - Serval vs Monster 题意 做法 程序 B - Common Raccoon vs Monster 题意 做 ...

  8. AtCoder Beginner Contest 177 题解

    AtCoder Beginner Contest 177 题解 目录 AtCoder Beginner Contest 177 题解 A - Don't be late B - Substring C ...

  9. AtCoder Beginner Contest 173 题解

    AtCoder Beginner Contest 173 题解 目录 AtCoder Beginner Contest 173 题解 A - Payment B - Judge Status Summ ...

随机推荐

  1. 串口应用:遵循uart协议发送N位数据(状态优化为3个,适用任意长度的输入数据,取寄存器中的一段(用变量作为边界))

    上一节中成功实现了发送多个字节的数据.把需要发送的数据分成多段遵循uart协议的数据依次发送.上一节是使用状态机实现的,每发一次设定为一个状态,所以需要发送的数据越多,状态的个数越多,代码越长,因而冗 ...

  2. CF455ABoredom

    题目大意: 给你一个由 \(n\) 个整数构成的序列 \(a\),玩家可以进行几个步骤,每一步他可以选择序列中的一个元素(我们把它的值定义为 \(a_k\))并删除它,此时值等于 \(a_{k + 1 ...

  3. C# 基础知识-特性

    C基础 - 特性 一.特性 1>特性本质就是一个类,直接或者间接的继承了Attribute 2>特性就是在不破话类封装的前提下,加点额外的信息或者行为 特性添加后,编译会在元素内部产生IL ...

  4. 获取字典中values值中最大的数,返回对应的keys

    1.字典中键值对的获取 print(data.values()) # 查看字典的值 print(data.keys()) # 查看字典的key 2.对字典中的值进行排序 sorted(data.val ...

  5. 【原创】Magisk+Shamiko过APP ROOT检测

    本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! Magisk+Shamiko过APP R ...

  6. google nexus5x 刷机抓包逆向环境配置(一)

    本文仅供学习交流使用,如侵立删! google nexus5x 刷机抓包逆向环境配置(一) 操作环境 nexus5x kaliLinux win10 准备 官方工具包官方下载地址:https://dl ...

  7. 使用.NET简单实现一个Redis的高性能克隆版(七-完结)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  8. Linux 01 概述

    参考源 https://www.bilibili.com/video/BV187411y7hF?spm_id_from=333.999.0.0 版本 本文章基于 CentOS 7.6 简介 Linux ...

  9. git submodule 实战

    1.git submodule指什么 关于git submodule是什么,可以看下面这个链接. https://www.cnblogs.com/hwx0000/p/14146838.html 2.g ...

  10. WebMvcConfigurerAdapter过时替换接口或类

    (注意!)WebMvcConfigurerAdapter 在spring 5.0中已经弃用了. 原来的使用方式 @Deprecated public abstract class WebMvcConf ...