标签:none 生成 处理 接下来 维护 100% nbsp style play
非常奇妙的倍增题
知名科学家小A在2118年在计算机上实现了模拟聚变的过程。我们将她研究的过程简化。核子共有26种,可以用a到z共26个字母表示。核子聚变的过程可以用一个字符串描述。按照顺序从左到右的顺序,假如有两个相同核子相邻,两个核子就会相互吸引发生聚变生成一个序号+1的核子,特殊的,两个z核子相邻会湮灭没有新的核子生成。每当两个核子聚变时,就需要重新从左到右重复找到两个相邻的相同核子直到不存在为止。比如zyzzy->zyy->zz->小A为了做出足够有效的实验,每次会从一个字符串中选定一个子串操作。她想要知道每次实验这个子串中的核子能否最终全部湮灭。
第一行一个只有小写字母的字符串。第二行一个数nn表示询问次数接下来nn行每行两个正整数li,rili,ri表示询问区间
对每次询问输出一行Yes或No表示答案
yzyyyzyzyyyz
8
1 6
7 12
1 12
6 11
1 1
1 3
4 9
3 8
Yes
Yes
Yes
Yes
No
No
No
No
L表示字符串长度对于30%的数据满足L<=100
对于60%的数据满足L<=3000,n<=3000
另存在20%数据满足字符串中只存在y,z对于
100%的数据,L<=500000,n<=1000000
第一眼看上去是个数据结构题……但是很明显这题的状态数非常多,并且区间信息也难以合并,所以所有基于序列长度的维护都是要挂的。
这题妙就是在于它用倍增维护基于结果的区间信息。(听上去很高端的样子实际上是不难理解的)
暴力的$O(n^2)$做法可以得60pts。具体实现可以用栈也可以用链表。
大概就是这个样子。
1 bool check(int l, int r) 2 { 3 tot = n, head = l; 4 for (int i=l; i<=r; i++) 5 pre[i] = i-1, nxt[i] = i+1; 6 pre[l] = -1, nxt[r] = -1; 7 for (;;) 8 { 9 bool fl = 0; 10 if (head==-1) return 1; 11 if (nxt[head]==-1) return 0; 12 for (int now=head; nxt[now]!=-1; now=nxt[now]) 13 if (a[now]==a[nxt[now]]){ 14 fl = 1; 15 if (a[now]==numz){ 16 int ss = pre[now], tt = nxt[nxt[now]]; 17 if (head==now){ 18 head = tt; 19 if (tt==-1) return 1; 20 pre[tt] = -1; 21 } 22 else{ 23 nxt[ss] = tt; 24 if (tt!=-1) pre[tt] = ss; 25 } 26 }else{ 27 a[++tot] = a[now]*2; 28 int ss = pre[now], tt = nxt[nxt[now]]; 29 if (head==now){ 30 head = tot; 31 if (tt==-1) return 0; 32 pre[tt] = tot, nxt[tot] = tt; 33 } 34 else{ 35 nxt[ss] = tot, pre[tot] = ss, nxt[tot] = tt; 36 if (tt!=-1) pre[tt] = tot; 37 } 38 } 39 break; 40 } 41 if (!fl) return 0; 42 } 43 }
用$nxt[i]$表示以$i$为左端点第一次消完的区间右端点。那么只要预处理出这个$nxt[]$就可以做到快速查询了————然而查询时也有可能被例如$zzzzzzzzz...$的数据卡飞,不过对于随机数据已经做得够好了。
先不管$zzzzz...$的情况,来考虑如何处理$nxt[]$。
用$t_{i,char}$表示$i$位置往后第一次遇到$char$字符的位置,这个是用来处理“聚变”的过程。那么显然这个可以用倍增维护。
处理出$t_{i,char}$之后,$nxt[i]$就等于$t_{i,z+1}$,这里我们把$z$聚变后也看做一个虚拟的字符。
既然一维的$nxt[]$会被卡挂,那么同时处理消去好几次后的$nxt[]$(等同于跳了多次)呢?
那么$nxt[i][j]$就表示以$i$开头消去$j$后的位置。
重要的细节
这里有一组数据
标签:none 生成 处理 接下来 维护 100% nbsp style play
原文地址:https://www.cnblogs.com/antiquality/p/9296599.html