抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题意

原题链接

给定一个序列的前\(n\)项(\(a_0,a_1,a_2,\cdots,a_{n-1}\)),保证对于所有\(0\le i\le n-1\)\(a_i\in \{n-1,n,n+1\}\)。对于\(i\ge n\)\(a_i\)的值为满足\(0\le j< i\)\(a_j\ge i-j\)的整数\(j\)的数量。

\(q\)个询问,每次询问一个\(x\),求\(a_x\)的值。

\(1\le n,q\le 10^5,0\le x\le 10^{15}\)

题解

首先,我们可以利用归纳法证明对于任意\(i\)\(a_i\in \{n-1,n,n+1\}\)

假设对于\(0\le j< i\)都满足该性质,则有\[a_i=n-[a_{i-n}=n-1]+[a_{i-n-1}=n+1]\]显然仍有\(a_i\in \{n-1,n,n+1\}\)

为方便讨论,用\(0,1,2\)分别代替\(n-1,n,n+1\)

可以发现,假设初始时对于所有\(i\ge n\)\(a_i=0\)。然后对于一个\(a_i\),若\(a_i=0\),则不产生贡献;若\(a_i=1\),则对\(a_{i+n}\)产生\(1\)的贡献;若\(a_i=2\),则对\(a_{i+n},a_{i+n+1}\)各产生\(1\)的贡献。

然后我们先考虑一些子问题。

  1. 若初始时\(a_i\in \{0,1\}\),由于不存在\(2\),一定有\(a_{i+n}=a_i\),所以直接\(\bmod n\)即可。

  2. 若初始时\(a_i\in \{1,2\}\),由于不存在\(0\),即不存在没有贡献的情况,所以对于\(a_i=2\),一定会导致\(a_{i+n}=1,a_{i+n+1}=2\),观察一下可以发现,此时\(a_{i+n+1}=a_i\),所以直接\(\bmod (n+1)\)即可。

考虑原问题,由于存在\(0\),将会导致有些位置没有贡献,那么对于\(a_i=2\),可能会导致\(a_{i+n}=1,a_{i+n+1}=1\)(即\(a_{i+1}=0\)时)。联系之前的两个问题,当不存在\(2\)时,这种情况下一定会导致\(a_{i+n+1}=0\);当不存在\(0\)时,这种情况下一定会导致\(a_{i+n+1}=2\)。可是,当\(2,0\)同时存在时,就会导致\(a_{i+n+1}=1\)

更形象一点地讲,把\(2\)当作石头,把\(1\)当作平地,把\(0\)当作洞,这种情况就是石头调入洞里形成平地的情况。

假设初始数组中第\(i\)个位置是洞,第\(j\)个位置是石头,石头\(j\)会在\(x\)位置与洞\(i\)重合,掉入洞中。又因为将会在所有满足\(p\equiv j\pmod{(n+1)}\)\(p\le x\)的位置\(p\)出现石头,洞同理。所以\(x\)一定是满足下列同余方程组的最小正整数:

\[\begin{cases} x\equiv i\pmod{n}\\x\equiv j\pmod{(n+1)} \end{cases}\]

这个求解想怎么做怎么做吧...

然后由于会首尾拼接,我们把初始数组想成一个环,显然对于每个洞\(i\),一定是在环上与\(i\)有向距离最短的石子最终掉落在洞\(i\)。那么我们直接扫两遍,用栈维护当前的石子(第二遍不清空栈,就相当于成了环),每次遇到洞就取出栈顶元素——石头\(j\)(如果栈为空则跳过),算出\(x\)的值,记录\(mat[i]=mat[j]=x\),一个点若没有\(mat\)值,则记为\(INF\)

\(mat\)的定义显而易见,\(mat[i]\)表示与\(i\)有向距离最近的石子/洞与\(i\)抵消的位置。

有了\(mat\)以后对于询问就可以\(\mathcal O(1)\)求了,可以根据\(mat[x\bmod n],mat[x\bmod (n+1)]\)\(x\)的大小关系来计算贡献,注意除了比较\(mat\)\(x\)的大小外,还需判断初始是否为石子/洞。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
long long read(){
register long long x = 0;
register char f = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f ^= 1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
#define N 200005
int n, q, a[N], top, sta[N];
long long x, mat[N]; // 注意long long
int main(){
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
int T = 1; // CC原题是多组数据,为了方便修改
while (T--){
n = read(), q = read();
for (register int i = 0; i < n; ++i) a[i] = read();
for (register int i = 0; i < n; ++i) mat[i] = 0x3f3f3f3f3f3f3f3fll;
top = 0;
for (register int k = 0; k < 2; ++k)
for (register int i = 0; i < n; ++i)
if (a[i] == n - 1 && top){ // 是洞,且栈非空
int j = sta[top--]; // 取出栈顶元素
mat[j] = mat[i] = (1ll * (i + n) * (n + 1) - 1ll * j * n) % (1ll * n * (n + 1));
// 根据中国剩余定理计算mat
}
else if (a[i] == n + 1) sta[++top] = i; // 石子入栈
while (q--){
x = read();
int ho = x % n, st = x % (n + 1);
printf("%d ", n - (a[ho] == n - 1 && mat[ho] > x) + (a[st] == n + 1 && mat[st] > x));
// O(1)计算答案,注意特判
}
putchar('\n');
}
}

评论