@description@

JOHNKRAM 和 C_SUNSHINE 在玩一个游戏。

游戏规则如下:有若干堆石子,游戏前选定一个正整数 p,JOHNKRAM 先手,两个人轮流操作。定义一次操作是选择某一堆石子,然后拿出其中的 p^k(k∈N) 个石子扔掉,不能操作者输。

C_SUNSHINE 表示判定谁能赢太简单了,于是他放了 n 堆石子,编号为 1∼n。

他每次把编号在某个区间内的石子堆加上若干个石子,或者询问以编号在某个区间内的石子堆进行游戏,是谁胜利。

JOHNKRAM 表示他不会做,于是他来向你求助。

input

第一行三个数 n, q, p,n 表示序列的长度,q 表示接下来操作的次数,p 的意义如题目描述中所说。

接下来一行 n 个数,第 i 个数表示初始时第 i 堆石子的石子数量。

接下来 q 行每行第一个数t表示操作类型,t=0 表示修改,t=1 表示询问。

对于一个修改操作,该行还会有三个数 l, r, x,表示把 [l…r] 的所有石子堆加上 x 个石子(博主注:x 为正数且属于 int 范围)。

对于一个询问操作,该行还会有两个数 l, r,表示询问以 [l…r] 的所有石子堆进行游戏是谁胜。

保证 1 <= n, q, p, 每堆石子的初始数量 <= 10^5。

output

对于每一个询问,如果 JOHNKRAM 胜利输出 1,否则输出 0。

sample input

10 9 3

2 6 2 5 8 7 4 3 4 1

1 1 10

0 5 7 15

1 1 3

0 3 9 11

1 3 7

0 4 5 53

0 1 2 26

1 6 10

1 4

sample output

0

0

0

1

0

@solution@

@part - 1@

通过 对sg函数打表 简单推导可得:

(1)当 p 为奇数时,sg函数循环节长度为 2,循环节为 01。即:

当 x % 2 == 0 时,sg[x] = 0。

当 x % 2 == 1 时,sg[x] = 1。

(2)当 p 为偶数时,sg函数循环节长度为 (p+1),循环节为 010101……01012。即:

当 x % (p+1) == p 时,sg[x] = 2。

当 (x % (p+1)) % 2 == 1 时,sg[x] = 1。

当 x % (p+1) != p 且 (x % (p+1)) % 2 == 0 时,sg[x] = 0。

简要(感性地)证明一下吧:

首先我们可以发现,当 x < p 时,只能从 sg[x-1] 到 sg[x],故一定形如 010101……

当 p 为奇数时,p^k 肯定也为奇数,即我们总是只能从偶数转移到奇数,奇数转移到偶数,故只能是 010101……

当 p 为偶数时,可以发现 p^k = (-1)^k (mod p+1),于是我们可以在 mod p+1 的意义下从 x+1 与 x-1 转移到 x。故当 x % (p+1) == p 时,sg[x] 只能等于 2。

于是可以得证。

@part - 2@

根据 sg 函数应用于博弈论的结论,我们必须在询问时求出区间 [l, r] 中 sg 函数的异或和才能判定胜负。

奇数很好办,写一个支持区间反转,统计区间中 1 的个数的线段树即可。

考虑偶数。我们需要在支持模意义下区间加操作时,至少可以询问一个区间内值等于 p 的个数(即 sg[x] = 2 的个数)。

我不知道线段树能不能搞。这显然是分块来搞。修改时整块加法标记,散块暴力重构;查询时整块通过加法标记寻找,散块暴力遍历。

再考虑求出一个区间内值为奇数的个数(即 sg[x] = 1 的个数)。考虑重构时储存值为奇数与值为偶数的两个有序数列,这样整块在加法标记下,新奇数序列必然是原奇数序列的某后缀接上原偶数序列的某前缀,或反过来原偶数序列的某后缀接上原奇数序列的某前缀。新偶数序列同理。

于是我们根据加法标记二分查找一下,就可以求出 sg[x] = 1 的个数了。

显然奇数也可以分块来搞,所以我们就可以不用写线段树了。

总复杂度 O(nsqrt(n)log n),有些卡但开了O2大概3s能过。

标算给出了一个 O(nsqrt(nlog n)) 的算法,不过太难写了。

大概就是在重构时充分利用原奇偶序列的有序性,归并两个有序数列可以在线性时间内完成,于是可以线性时间完成重构。然后再重新调整块的大小使询问与修改的复杂度平衡。

这个常数说不定还不如上面那个算法……

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
#define lb lower_bound
const int BLOCK = 320;
const int MAXN = 100000;
int le[MAXN/BLOCK + 5], ri[MAXN/BLOCK + 5], add[MAXN/BLOCK + 5], num[MAXN + 5];
int arr[2][MAXN/BLOCK + 5][BLOCK + 5], siz[2][MAXN/BLOCK + 5];
int n, q, p, bcnt;
int a[MAXN + 5];
void rebuild(int x) {
siz[0][x] = siz[1][x] = 0;
for(int i=le[x];i<=ri[x];i++)
arr[a[i]&1][x][++siz[a[i]&1][x]] = a[i];
sort(arr[0][x] + 1, arr[0][x] + siz[0][x] + 1);
sort(arr[1][x] + 1, arr[1][x] + siz[1][x] + 1);
}
void build() {
bcnt = (n-1)/BLOCK + 1;
for(int i=0;i<n;i++) {
if( i % BLOCK == 0 )
le[i/BLOCK+1] = ri[i/BLOCK+1] = i;
else ri[i/BLOCK+1]++;
num[i] = i/BLOCK+1;
}
for(int i=1;i<=bcnt;i++)
rebuild(i);
}
void pushdown(int x) {
for(int i=le[x];i<=ri[x];i++)
a[i] = (a[i] + add[x])%(p+1);
add[x] = 0;
}
int fun(int x) {
if( x == p ) return 2;
else return (x&1);
}
int main() {
freopen("right.in", "r", stdin);
freopen("right.out", "w", stdout);
scanf("%d%d%d", &n, &q, &p);
if( p % 2 == 1 ) p = 1;
for(int i=0;i<n;i++)
scanf("%d", &a[i]), a[i] %= (p+1);
build();
for(int i=1;i<=q;i++) {
int tp; scanf("%d", &tp);
if( tp == 0 ) {
int l, r, x; scanf("%d%d%d", &l, &r, &x), l--, r--, x %= (p+1);
if( num[l] == num[r] ) {
pushdown(num[l]);
for(int i=l;i<=r;i++)
a[i] = (a[i] + x)%(p+1);
rebuild(num[l]);
}
else {
for(int i=num[l]+1;i<=num[r]-1;i++)
add[i] = (add[i] + x)%(p+1);
pushdown(num[l]);
for(int i=l;i<=ri[num[l]];i++)
a[i] = (a[i] + x)%(p+1);
rebuild(num[l]);
pushdown(num[r]);
for(int i=le[num[r]];i<=r;i++)
a[i] = (a[i] + x)%(p+1);
rebuild(num[r]);
}
}
else {
int l, r, ans = 0; scanf("%d%d", &l, &r), l--, r--;
if( num[l] == num[r] ) {
for(int i=l;i<=r;i++)
ans ^= fun((a[i] + add[num[i]])%(p+1));
}
else {
for(int i=l;i<=ri[num[l]];i++)
ans ^= fun((a[i] + add[num[i]])%(p+1));
for(int i=le[num[r]];i<=r;i++)
ans ^= fun((a[i] + add[num[i]])%(p+1));
for(int i=num[l]+1;i<=num[r]-1;i++) {
int t1 = p-add[i];
int x = upper_bound(arr[t1&1][i]+1, arr[t1&1][i]+siz[t1&1][i]+1, t1) - arr[t1&1][i];
int y = lower_bound(arr[t1&1][i]+1, arr[t1&1][i]+siz[t1&1][i]+1, t1) - arr[t1&1][i];
int z = upper_bound(arr[t1&1^1][i]+1, arr[t1&1^1][i]+siz[t1&1^1][i]+1, t1) - arr[t1&1^1][i];
if( (x-y) & 1 ) ans ^= 2;
if( p != 1 && ((z-1+siz[t1&1][i]-x+1) & 1) ) ans ^= 1;
}
}
puts(ans ? "1" : "0");
}
}
}
/*
以下是打表用程序:
#include<cstdio>
const int MAXN = 100;
int sg[MAXN + 5];
bool tag[MAXN + 5];
int main() {
int p; scanf("%d",Q &p);
sg[0] = 0;
for(int i=1;i<=MAXN;i++) {
for(int j=0;j<=MAXN;j++)
tag[j] = false;
tag[sg[i-1]] = true;
if( p != 1 ) {
int x = p;
while( x <= i ) {
tag[sg[i-x]] = true;
x *= p;
}
}
for(int j=0;;j++)
if( !tag[j] ) {
sg[i] = j;
break;
}
}
for(int i=0;i<=MAXN;i++)
printf("%d : %d\n", i, sg[i]);
}
*/

@details@

康复计划 - 9。

当我对博弈论一筹莫展时,旁边的 zxb 大佬小声地提醒我说:“打表”。突然就幡然醒悟了。

这场比赛的 T2 我还没写,因为我还没有理解随机状况下标算时间复杂度怎么来的。以后慢慢填坑(咕咕咕)。

@NOI模拟2017.06.30 - T3@ Right的更多相关文章

  1. @NOI模拟2017.06.30 - T1@ Left

    目录 @description@ @solution@ @accepted code@ @details@ @description@ JOHNKRAM 最近在研究排序网络,但他发现他不会制作比较器, ...

  2. @NOI模拟2017.07.02 - T1@ Attack

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 『新的风暴已经出现,怎么能够停滞不前』--你决定去攻击小怪兽的巢 ...

  3. 5.30 NOI 模拟

    $5.30\ NOI $模拟 高三大哥最后一次模拟考了,祝他们好运 \(T1\)装箱游戏 显然可以将四种字母之间的空缺当做状态枚举 那么这道题就很显然了 #include<bits/stdc++ ...

  4. 5.23 NOI 模拟

    $5.23\ NOI $模拟 \(T1\)简单的计算几何题 \(zjr:\)我当时没改,那么自己看题解吧 倒是有个简单的随机化方法(能获得\(72pts,\)正确性未知)\(:\) 随机两条切椭圆的平 ...

  5. 5.6 NOI模拟

    \(5.6\ NOI\)模拟 明天就母亲节了,给家里打了个电话(\(lj\ hsez\)断我电话的电,在宿舍打不了,只能用教练手机打了) 其实我不是很能看到自己的\(future,\)甚至看不到高三的 ...

  6. 5.4 NOI模拟

    \(5.4\ NOI\)模拟 \(T1\) 想到分讨,但是暴力输出一下方案之后有很多特别的情况要讨论,就弃了... 假设\(a\)是原序列,\(b\)是我们得到的序列 设\(i\)是最长公共前缀,\( ...

  7. hdu6034[模拟] 2017多校1

    /*hdu6034[模拟] 2017多校1*/ //暴力模拟26个26进制数即可, 要注意进位 #include<bits/stdc++.h> using namespace std; t ...

  8. 【2018.12.10】NOI模拟赛3

    题目 WZJ题解 大概就是全场就我写不过 $FFT$ 系列吧……自闭 T1 奶一口,下次再写不出这种 $NTT$ 裸题题目我就艹了自己 -_-||| 而且这跟我口胡的自创模拟题 $set1$ 的 $T ...

  9. 【NOI】2017 蚯蚓排队(BZOJ 4943,LOJ 2303) 模拟+hash

    [题目]#2303. 「NOI2017」蚯蚓排队 [题意]给定n条长度不超过6的蚯蚓,初始各自在一个队伍.m次操作:1.将i号蚯蚓和j号蚯蚓的队伍合并(保证i为队尾,j为队首).2.将i号蚯蚓和它后面 ...

随机推荐

  1. IO流5 --- FileReader读入数据的基本操作 --- 技术搬运工(尚硅谷)

    FileReader 字符输入流 @Test public void test1(){ File file = new File("hello.txt"); FileReader ...

  2. python基--re模块的使用

    正则表达式: 正则表达式本身是一种小型的.高度专业化的编程语言,然而在python中,通过内嵌集成re模块让调用者们可以直接调用来实现正则匹配.正则表达模式被变异成一系列的字节码,然后由C语言编写的 ...

  3. html 遮罩层以及弹出框的制作

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. Django用户名密码错误提示

    from django.shortcuts import render # Create your views here. from django.shortcuts import render fr ...

  5. webpack学习之—— Plugins

    Plugins are the backbone of webpack! webpack 自身也是构建于你在 webpack 配置中用到的相同的插件系统之上! 插件目的在于解决 loader 无法实现 ...

  6. nginx反项代理的简单配置

    在ubuntu 16.04下安装nginx, apt-get install nginx就可以了. 然后安装了node, npm, 写了个简单的main.js,启动了一个http,并监听 8888 然 ...

  7. 当node升级后导致webpack打包出错,node-saas出问题的解决办法

    报错信息如下: ERROR in ./node_modules/_extract-text-webpack-plugin@3.0.2@extract-text-webpack-plugin/dist/ ...

  8. php实现图片以base64显示的方法达到效果

    目前Data URI scheme支持的类型有: data:text/plain,文本数据data:text/html,HTML代码data:text/html;base64,base64编码的HTM ...

  9. Directx11教程(63) tessellation学习(5)

    原文:Directx11教程(63) tessellation学习(5)        TS中生成细分后顶点的u,v,{w}坐标,我们根据控制点和u,w,{w}坐标生成新的顶点位置,在前面四边形的细分 ...

  10. 洛谷P2062 分队问题

      这是一道普及/提高- 然后你懂的,贪心扫一遍就可以了. 不懂提交人数那么少. //Serene #include<algorithm> #include<iostream> ...