令先手为$A$,后手为$B$,将相邻同色棋子合并成块,首先特判一些情况:

  1. 如果所有格子都是满的,那么显然$A$必败。
  2. 否则如果所有块都只有一个棋子,那么显然平局。
  3. 枚举$A$的第一步操作,如果可以使得B无法操作,那么显然$A$必胜。

无视所有大小为$1$的块,考虑剩下块里相邻两块,它们往外扩张比往内缩更优:

  • 如果是形如[A_A]___B__A__BA____B___[AA_A],两边同色,所以中间这些块(包括位置)可以删除,不影响游戏结果。
  • 如果是形如[A_A]___B__A__B_A__[BB_B],两边异色,那么相邻两块相互靠近更优,可以等效替代成[A_A]___[BB][AA]__[BB][AA]__[BB_B]。

经过上述转化后,不再存在大小为$1$的块,当存在大小至少为$3$且内部存在空隙的自由块时,该块显然可以永远操作下去。假设不存在自由块,那么显然不会平局,游戏等价于每堆石子数为相邻两块间距的Nim游戏。

分以下情况讨论:

  1. $A$可以通过至多一步操作得到自由块,且可以阻止$B$得到自由块,此时结果为$A$胜。
  2. $AB$一开始都没有自由块,但都可以通过一步得到,$A$先堵$B$,$B$再堵$A$后双方都没有自由块,但是此时Nim游戏$A$胜,则最终$A$胜。
  3. $AB$都没法通过一步操作得到自由块,但是此时Nim游戏$A$胜,则最终$A$胜。
  4. $A$可以通过至多一步操作得到自由块,但是阻止不了$B$得到自由块,此时结果为平局。
  5. $A$无法得到自由块,也阻止不了$B$得到自由块,此时结果为$B$胜。
  6. 双方都没法得到自由块,但是此时Nim游戏$B$胜,则最终$B$胜。

时间复杂度$O(B+C)$。

#include<cstdio>
#include<cstring>
#define CLR(x) memset(x,0,sizeof x)
const int N=1000010,BUF=12000000;
char Buf[BUF],*buf=Buf;
int Case,m,n,ce,A,B,i,j,k,o,x,st,a[N],b[N],can10,can11,can01,can00;
struct P{
int x;char col;
P(){}
P(int _x,char _col){x=_x,col=_col;}
}q[N];
struct E{
int len,cnt,dis;char col;
E(){}
E(int _len,int _cnt,int _dis,char _col){len=_len,cnt=_cnt,dis=_dis,col=_col;}
bool free(){return len>cnt&&cnt>=3;}
}e[N];
int cfree[2],cmove[2],cnt[2],cmay[2],sum[2],CFREE,CMAY0,CMAY1;
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
inline void cal(){
if(e[0].col==e[ce-1].col){
ce--;
e[0].len+=e[ce].len+e[ce].dis;
e[0].cnt+=e[ce].cnt;
}
CLR(cfree),CLR(cmove),CLR(cnt),CLR(cmay);CLR(sum);
for(i=0;i<ce;i++){
cnt[e[i].col]++;
if(e[i].free())cfree[e[i].col]++,cmove[e[i].col]++,cmay[e[i].col]++;
if(e[i].len>e[i].cnt)cmove[e[i].col]++;
if(e[i].dis){
cmove[e[i].col]++;
if(e[i].cnt>=3){
cmay[e[i].col]++;
sum[e[i].col]^=e[i].dis;
}
}
if(e[(i-1+ce)%ce].dis){
cmove[e[i].col]++;
if(e[i].cnt>=3){
cmay[e[i].col]++;
sum[e[i].col]^=e[(i-1+ce)%ce].dis;
}
}
}
}
inline bool can_block_b(){
if(cfree[1])return 0;
if(cmove[1]>1)return 0;
for(i=0;i<ce;i++)if(e[i].col==1&&e[i].len>e[i].cnt)return 0;
for(i=0;i<ce;i++)if(e[i].col==1){
if(e[i].dis&&e[(i+1)%ce].len>1)return 1;
if(e[(i-1+ce)%ce].dis&&e[(i-1+ce)%ce].len>1)return 1;
}
return 0;
}
inline void change(int x,int y){
CFREE=cfree[0],CMAY0=cmay[0],CMAY1=cmay[1];
if(e[x].cnt>=3)CFREE++,CMAY0++;
if(e[y].cnt>=3&&!e[y].free())CMAY1--;
}
inline int solve(){
read(m),read(A),read(B);
for(i=1;i<=A;i++)read(a[i]);
for(i=1;i<=B;i++)read(b[i]);
//Circle is full, so A can't move
if(A+B==m)return -1;
n=0;
i=j=1;
while(i<=A&&j<=B)if(a[i]<b[j])q[++n]=P(a[i++],0);else q[++n]=P(b[j++],1);
while(i<=A)q[++n]=P(a[i++],0);
while(j<=B)q[++n]=P(b[j++],1);
ce=0;
for(i=1;i<=n;i=j+1){
for(j=i;j<n&&q[i].col==q[j+1].col;j++);
e[ce++]=E(q[j].x-q[i].x+1,j-i+1,q[j+1].x-q[j].x-1,q[i].col);
}
e[ce-1].dis=q[1].x-q[n].x-1+m;
cal();
//All blocks' length is 1, draw
for(i=0;i<ce;i++)if(e[i].len!=1)break;
if(i>=ce)return 0;
//A can't move
if(!cmove[0])return -1;
//A can make B not to move
if(can_block_b())return 1;
for(i=0;i<ce;i++)if(e[i].len>1){st=i;break;}
for(i=0;i<ce;i=j){
for(j=i+1;j<=ce;j++)if(e[(st+j)%ce].len>1)break;
int l=(st+i)%ce;
if(e[l].col!=e[(st+j)%ce].col)for(k=i+1,o=1;k<j;k++,o^=1){
x=(st+k)%ce;
if(o)e[x].dis=0;
e[x].len=e[x].cnt=2;
}else{
e[l].len+=e[l].dis;
e[l].dis=0;
for(k=i+1,o=1;k<j;k++,o^=1){
x=(st+k)%ce;
if(o)cnt[e[x].col]--;
e[l].len+=e[x].len+e[x].dis;
e[l].cnt+=e[x].cnt;
}
}
}
//No such blocks
if(!cnt[0])return -1;
if(!cnt[1])return 1;
for(i=n=0;i<ce;i++)if(e[i].len>1)e[n++]=e[i];
for(ce=i=1;i<n;i++)if(e[i].col==e[ce-1].col){
e[ce-1].len+=e[i].len+e[ce-1].dis;
e[ce-1].cnt+=e[i].cnt;
e[ce-1].dis=e[i].dis;
}else e[ce++]=e[i];
cal();
//A can't move
if(!cmove[0])return -1;
//A can make B not to move
if(can_block_b())return 1;
can10=can11=can01=can00=0;
CFREE=cfree[0],CMAY0=cmay[0],CMAY1=cmay[1];
if(CFREE){
if(!CMAY1)can10=1;
if(cfree[1])can11=1;
}
for(x=i=0;i<ce;i++)x^=e[i].dis;
for(i=0;i<ce;i++)if(e[i].col==0){
if(e[i].dis){
change(i,(i+1)%ce);
if((CMAY0>1||CFREE)&&!CMAY1)can10=1;
if(CFREE&&CMAY1)can11=1;
if(!CFREE&&CMAY1)can01=1;
if(!CMAY1&&x==e[i].dis)can00=1;
if(!CFREE&&CMAY0==1&&!CMAY1)if(x^e[i].dis^sum[0])can00=1;
}
if(e[(i-1+ce)%ce].dis){
change(i,(i-1+ce)%ce);
if((CMAY0>1||CFREE)&&!CMAY1)can10=1;
if(CFREE&&CMAY1)can11=1;
if(!CFREE&&CMAY1)can01=1;
if(!CMAY1&&x==e[(i-1+ce)%ce].dis)can00=1;
if(!CFREE&&CMAY0==1&&!CMAY1)if(x^e[(i-1+ce)%ce].dis^sum[0])can00=1;
}
}
if(can10)return 1;
if(can00)return 1;
if(can11)return 0;
if(can01)return -1;
return x?1:-1;
}
int main(){
fread(Buf,1,BUF,stdin);read(Case);
while(Case--){
int x=solve();
if(x>0)puts("B");
if(!x)puts("R");
if(x<0)puts("C");
}
return 0;
}

  

BZOJ3497 : Pa2009 Circular Game的更多相关文章

  1. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  2. 如何在Spring MVC Test中避免”Circular view path” 异常

    1. 问题的现象 比如在webConfig中定义了一个viewResolver public class WebConfig extends WebMvcConfigurerAdapter { //配 ...

  3. Circular Buffer

    From:http://bradforj287.blogspot.com/2010/11/efficient-circular-buffer-in-java.html import java.util ...

  4. 在.NET Core中遭遇循环依赖问题"A circular dependency was detected"

    今天在将一个项目迁移至ASP.NET Core的过程中遭遇一个循环依赖问题,错误信息如下: A circular dependency was detected for the service of ...

  5. Circular progress bar in Unity 3D

    Circular progress bar in Unity 3D - UnityScripthttp://stackoverflow.com/questions/22662706/circular- ...

  6. A CIRCULAR PROGRESSBAR STYLE USING AN ATTACHED VIEWMODEL

    This blog post describes how to re-template the Silverlight ProgressBar control to render a circular ...

  7. Linux平台Qt creator报错:Circular all <- first dependency dropped

    在Linux下安装好Qt 5.0之后,使用Qt Creator创建了一个基于QMainWindow的框架程序.原本应该可以顺利的完成编译工作,因为自带的模板工程没有经过任何修改.可是在编译整个工程的时 ...

  8. The Bip Buffer - The Circular Buffer with a Twist

    Introduction The Bip-Buffer is like a circular buffer, but slightly different. Instead of keeping on ...

  9. org.apache.http.client.CircularRedirectException: Circular redirect to "http://xxx"问题解决

      org.apache.http.client.CircularRedirectException: Circular redirect to "http://xxx"问题解决 ...

随机推荐

  1. HTTP高并发调优小记

    tomcat服务层 1.修改server.xml <Connector port="8088" protocol="HTTP/1.1" maxThread ...

  2. 【opencv实践】边缘检测

    边缘检测: 一.canny算子 Canny边缘检测根据对信噪比与定位乘积进行测度,得到最优化逼近算子,也就是Canny算子.类似与 LoG 边缘检测方法,也属于先平滑后求导数的方法. 二.canny算 ...

  3. IDEA (mac版)

    mac键:option=alt command=ctrl idea快捷键 command+Enter(get,set界面) command+alt+L (格式化代码) ctrl+shift+space ...

  4. 通过FileReader和FileWriter实现复制文件的方法。

    public class CopyDemo { public static void main(String []args) { copyd(); } public static  void copy ...

  5. 「luogu2387」[NOI2014] 魔法森林

    「luogu2387」[NOI2014] 魔法森林 题目大意 \(n\) 个点 \(m\) 条边的无向图,每条边上有两个权值 \(a,b\),求从 \(1\) 节点到 \(n\) 节点 \(max\{ ...

  6. pmi-ACP考试知识点梳理(部分)

    敏捷宣言 个体和互动 高于流程和工具 工作的软件 高于详尽的文档 客户合作 高于合同谈判 响应变化 高于遵循计划 十二条敏捷原则 1 我们最重要的目标,是通过持续不断地及早交付有价值的软件使客户满意. ...

  7. java学习笔记10-方法

    我们经常用到System.out.println(),它到底是什么? System是系统类 out是系统类的标准输出对象 println()是一个方法 也就是说是调用了System类中的标准输出对象o ...

  8. char *p[] 和char**的思考

    char *p[] = {"hello","world"}; char **pp; pp = p; printf("%s,%s\n",*pp ...

  9. 【MySQL】MySQL基础操作语句

    mysql基础操作语句,包括数据库的增.删.切换,以及表的增.删.改.查.复制. 创建数据库 mysql> create database tem; 使用数据库 mysql> use te ...

  10. 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]

    传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...