【学员专栏012期】题解 P5523 【[yLOI2019] 珍珠】-黄子睿

作者:黄子睿
ID:huangzirui
学校:长沙市中雅培粹新九年级
获奖:2019年提高组一等奖,普及组一等奖(满分)
博客地址:https://www.luogu.com.cn/blog/My-luoguBuoke-HZR/solution-p5523

题意:

定义运算 nand ( 非与 )为:

A nand B =not(A and B)

即:

nand 0 1
0 1
1 1 0

现在给你一个序列 A,要你支持以下操作:

  1. 从序列的左边或右边插入一个非 0 即 1 的数。

  2. 从序列的左边或右边开始,查询连续 x 个数的 非与 值

操作数 ≤10^7+6

(吐槽:原题输入格式太 duliu 了)


首先找一下 nand 操作有什么性质。

发现 0 nand 任何一个数都是 1 。

这意味着无论有多少个数做这个运算,只要运算的过程中有 0 ,那么不论前面有什么东西,到这个位置统统都变成 1 ,所以答案只和 0 后面的有关。(这意味着答案只和最后的 0 有关)

那么假如我们从右往左查询,我们只需要找到查询到的最后一个数,再从这个节点往前找到一个 0 ,最后计算从这个 0 到最后一个数的答案即可。

比如:

A = 1,0,1,0,1,1,1,1,0

我现在想从左往右查询 8 个数,那么查询到的最后一个数就是第八个数。第八个数左边的第一个 0 在第四个位置。然后从第四个位置向右 非与 即可。

又因为这样子计算相当于计算 1nand 1 nand 1 … nand 1 。这个表达式的值显然可以 O(1) 计算。

所以原问题就转化成求一个位置上向左和向右的第一个 0 的位置。又因为这道题插入的方式很奇特,这个东西可以从上一个数递推过来(也可以从新加入的数推过来)。具体的说:

设第 i 个位置向左第一个 0 的位置为 Li ,向右第一个位置是 Ri

当我们从左边加了一个 0 ,那就可以把这个 0 右边所有连续的 1 的 L 数组都设成这个位置。又因为每个 0 最多被一个 1 查找到,均摊复杂度是 O( 长度 ) 的。

当我们从左边加了一个 1 , Ri可以直接从 R(i+1)处得到。

然后算法大概分析完了。

这道题细节相当多,比如 0 nand 一个数答案肯定是 1 ,但是它单独一个数时就是 0 ,因此要特判一开始就是 0 的情况。

代码主体部分不长,输入部分占了一半,果然是输入格式防 AK 题。

#include <bits/stdc++.h>using namespace std;
namespace Maker {
typedef unsigned int uit;
bool __sp;uit __x, __y, __z;int __type, __k, __m;
const int L = 1 << 21;char buf[L], *front=buf, *end=buf;char GetChar() { if (front == end) { end = buf + fread(front = buf, 1, L, stdin); if (front == end) return -1; } return *(front++);}
template <typename T>inline void qr(T &x) { char ch = GetChar(), lst = ' '; while ((ch > '9') || (ch < '0')) lst = ch, ch = GetChar(); while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = GetChar(); if (lst == '-') x = -x;}
template <typename T>inline void Begin(const T &x) { __type = x % 10; qr(__x); qr(__y); qr(__z); qr(__m); __sp = (__type == 3) || (__type == 4); __type &= 1;}
inline uit __Next_Integer() { __x ^= __y << (__z & 31); __y ^= __z >> (__x & 31); __z ^= __x << (__y & 31); __x ^= __x >> 5; __y ^= __y << 13; __z ^= __z >> 6; return __x;}
inline uit Rand() { return __Next_Integer(); }
template <typename Tx, typename Ty, typename Tz>inline void Get_Nextline(Tx &x, Ty &y, Tz &z) { if (__m) { --__m; x = 0; y = 0; z = 0; qr(x); qr(y); qr(z); if (x == 0) ++__k; } else { x = Rand() & 1; y = Rand() & 1; if (__k == 0) { x = 0; } if (x == 0) { ++__k; if (__sp) { z = __type; } else { z = Rand() & 1; } } else { int dk = __k >> 1; if (dk == 0) { z = 1; } else { z = Rand() % dk + dk; } } }}}
const int maxn=3e7+10;
int Left[maxn],Right[maxn];int nowL=15000001,nowR=15000000;int i,j,k,n,m;int ans1,ans2,ans3,ans4;
int main() { scanf('%d', &n); Maker::Begin(n); memset(Left,-1,sizeof(Left)); memset(Right,-1,sizeof(Right)); int id=0; for (int x,y,z;n;--n){ ++id; int ans=-1; Maker::Get_Nextline(x, y, z); if(x==0 && y==0 && z==1){ nowL--; Left[nowL]=-1; Right[nowL]=Right[nowL+1]; }if(x==0 && y==0 && z==0){ nowL--; Left[nowL]=nowL; Right[nowL]=nowL; for(i=nowL+1;i<=nowR;i++){ if(Left[i]!=-1)break; Left[i]=nowL; } } if(x==0 && y==1 && z==1){ nowR++; Right[nowR]=-1; Left[nowR]=Left[nowR-1]; }if(x==0 && y==1 && z==0){ nowR++; Right[nowR]=nowR; Left[nowR]=nowR; for(i=nowR-1;i>=nowL;i--){ if(Right[i]!=-1)break; Right[i]=nowR; } } int Z=-1; if(x==1 && y==0){ z=z+nowL-1; Z=Left[z]; if(z==nowL){ if(Left[z]==-1)ans=1; else ans=0; }else{ if(Z==nowL)Z--; if(Z==-1)Z=nowL; if((z-Z)%2==0)ans=1; else ans=0; } } if(x==1 && y==1){ z=nowR-z+1; Z=Right[z]; if(z==nowR){ if(Right[z]==-1)ans=1; else ans=0; }else{ if(Z==nowR)Z++; if(Z==-1)Z=nowR; if((Z-z)%2==0)ans=1; else ans=0; } } if(ans==1)ans1++; if(ans==0 && id%2==1)ans2++; if(ans==1 && id%2==0)ans3++; if(ans==0 && id%1024==0)ans4++; }cout<<ans1<<' '<<ans2<<' '<<ans3<<' '<<ans4<<endl; return 0;}
(0)

相关推荐