@NOI模拟2017.06.30 - T3@ Right
@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的更多相关文章
- @NOI模拟2017.06.30 - T1@ Left
目录 @description@ @solution@ @accepted code@ @details@ @description@ JOHNKRAM 最近在研究排序网络,但他发现他不会制作比较器, ...
- @NOI模拟2017.07.02 - T1@ Attack
目录 @description@ @solution@ @accepted code@ @details@ @description@ 『新的风暴已经出现,怎么能够停滞不前』--你决定去攻击小怪兽的巢 ...
- 5.30 NOI 模拟
$5.30\ NOI $模拟 高三大哥最后一次模拟考了,祝他们好运 \(T1\)装箱游戏 显然可以将四种字母之间的空缺当做状态枚举 那么这道题就很显然了 #include<bits/stdc++ ...
- 5.23 NOI 模拟
$5.23\ NOI $模拟 \(T1\)简单的计算几何题 \(zjr:\)我当时没改,那么自己看题解吧 倒是有个简单的随机化方法(能获得\(72pts,\)正确性未知)\(:\) 随机两条切椭圆的平 ...
- 5.6 NOI模拟
\(5.6\ NOI\)模拟 明天就母亲节了,给家里打了个电话(\(lj\ hsez\)断我电话的电,在宿舍打不了,只能用教练手机打了) 其实我不是很能看到自己的\(future,\)甚至看不到高三的 ...
- 5.4 NOI模拟
\(5.4\ NOI\)模拟 \(T1\) 想到分讨,但是暴力输出一下方案之后有很多特别的情况要讨论,就弃了... 假设\(a\)是原序列,\(b\)是我们得到的序列 设\(i\)是最长公共前缀,\( ...
- hdu6034[模拟] 2017多校1
/*hdu6034[模拟] 2017多校1*/ //暴力模拟26个26进制数即可, 要注意进位 #include<bits/stdc++.h> using namespace std; t ...
- 【2018.12.10】NOI模拟赛3
题目 WZJ题解 大概就是全场就我写不过 $FFT$ 系列吧……自闭 T1 奶一口,下次再写不出这种 $NTT$ 裸题题目我就艹了自己 -_-||| 而且这跟我口胡的自创模拟题 $set1$ 的 $T ...
- 【NOI】2017 蚯蚓排队(BZOJ 4943,LOJ 2303) 模拟+hash
[题目]#2303. 「NOI2017」蚯蚓排队 [题意]给定n条长度不超过6的蚯蚓,初始各自在一个队伍.m次操作:1.将i号蚯蚓和j号蚯蚓的队伍合并(保证i为队尾,j为队首).2.将i号蚯蚓和它后面 ...
随机推荐
- oracle习题-emp表查询练习
emp表查询练习 1 查询emp表的全部记录 Select * from emp; 2 查询出每个雇员的编号.姓名.基本工资 Select empno,ename,sal from emp; 3 查询 ...
- mysql连接出现Unknown system variable 'tx_isolation'异常
出现这个异常,是因为mysql-connector-java.jar的版本太低,数据库的版本太高,不匹配导致的. 因此将mysql-connector-java升级到最新版本就解决了问题. 最新的三个 ...
- notepad++ 退出后关闭所有文档(关闭“记住最后打开的文件”)
旧版本: 设置->首选项->其他->取消勾选Remember current session for next launch 新版本: 设置->首选项->备份->取 ...
- eclipse修改中文注释的字体(亲测有用!)
Window –> Preferences –> General –> Appearance –> Colors and Fonts –> Basic –> Tex ...
- LeetCode169 Majority Element, LintCode47 Majority Number II, LeetCode229 Majority Element II, LintCode48 Majority Number III
LeetCode169. Majority Element Given an array of size n, find the majority element. The majority elem ...
- No.2 Verilog 模块和描述风格
2-1 模块 Verilog语言基本的描述单元----模块,模块是用来描述某个设计的功能或结构,以及它与其它外部模块进行通信的端口. module module_name(port_list); De ...
- python 数据分析领域常用的包
- Liferay OSGi注解(Annotation) - 使用手册(译文)
mark过来好好用 上Neil的博客地址Liferay OSGi注解(Annotation) - 使用手册(译文) @Component是注入服务 @Reference是获取服务(在Liferay框架 ...
- 【JZOJ4890】【NOIP2016提高A组集训第14场11.12】随机游走
题目描述 YJC最近在学习图的有关知识.今天,他遇到了这么一个概念:随机游走.随机游走指每次从相邻的点中随机选一个走过去,重复这样的过程若干次.YJC很聪明,他很快就学会了怎么跑随机游走.为了检验自己 ...
- acm一路走来的体验和想法
2017年参加天梯赛 作为大一萌新去参加人生第一场正规的比赛,学校拿钱砸的.我呢,是十分激动的,是时候检验下自己的编程水平了,也有一丝慌张,怕给团队抹黑. 然后呢,晒一下自己成绩. 我本来觉得我和他们 ...