初始编号为
i
∈
[
0
,
n
)
iin[0,n)
i∈[0,n)的格子上分别有
a
i
a_i
ai砖,每次把第
i
i
i格的
x
x
x块砖拿走,放到
[
m
a
x
(
i
+
1
,
n
)
,
i
+
x
]
[max(i+1,n),i+x]
[max(i+1,n),i+x]上,每格一块,多余的清空,
q
q
q次询问,每次询问操作到格子
q
i
q_i
qi时上面的砖块数量。
n
,
q
≤
1
0
5
n,qleq 10^5
n,q≤105,
q
i
≤
1
0
18
q_ileq10^{18}
qi≤1018,
a
i
∈
{
n
−
1
,
n
,
n
+
1
}
a_iin{n-1,n,n+1}
ai∈{n−1,n,n+1}
题解
找规律或归纳法证明可以得到每个格子的答案都只有
{
n
−
1
,
n
,
n
+
1
}
{n-1,n,n+1}
{n−1,n,n+1}三种情况,不妨把它们分别看作是
{
0
,
1
,
2
}
{0,1,2}
{0,1,2}。
可以每
n
n
n个格子分成一段,若前
i
−
1
i-1
i−1段的答案已经计算出来了,考虑如何计算第
i
i
i段的答案,
设
f
i
,
j
f_{i,j}
fi,j表示第
i
i
i段第
j
(
j
∈
[
0
,
n
)
)
j(jin[0,n))
j(j∈[0,n))个格子的答案,若
f
i
−
1
,
j
≥
1
f_{i-1,j}geq1
fi−1,j≥1,那么
f
i
,
j
f_{i,j}
fi,j可以加
1
1
1;若
f
i
−
1
,
j
−
1
=
2
f_{i-1,j-1}=2
fi−1,j−1=2,那么
f
i
,
j
f_{i,j}
fi,j可以加
1
1
1。特别地,若
f
i
−
2
,
n
−
1
=
2
f_{i-2,n-1}=2
fi−2,n−1=2,则
f
i
,
0
f_{i,0}
fi,0可以加
1
1
1。
再转化一下,相当于一段格子上叠有若干的砖块,从第
i
i
i段到第
i
+
1
i+1
i+1段的过程相当于把所有第二层的砖块向右推一格,当右边没有砖块时,该砖块就会掉落并不再被推动。特别地,第
i
i
i段的最后一格砖块向右推直接推到第
i
+
2
i+2
i+2段。
#include<cstdio>#include<cstring>#include<algorithm>usingnamespace std;#define N 100010#define ll long longint p[N], e[N], a[N], q[N];intmain(){int tn;scanf("%d",&tn);while(tn--){int n, m, i; ll x;scanf("%d%d",&n,&m);
q[0]=0;for(i =0; i < n; i++){scanf("%d",&a[i]), a[i]-= n -1;
e[i]=-1, p[i]=0;}for(i =0; i < n; i++){if(a[i]==2) q[++q[0]]= i;elseif(!a[i]&& q[0]){int t = q[q[0]];
e[t]= i, p[i]= i - t +1;
q[0]--;}}for(i =0; i < n; i++){if(!a[i]&&!p[i]&& q[0]){int t = q[q[0]];
e[t]= i, p[i]= n - t + i +2;
q[0]--;}}for(i =1; i <= m; i++){scanf("%lld",&x);
ll t = x / n +1;if(!a[x % n]&&(!p[x % n]|| p[x % n]> t))printf("%d ", n -1);else{int l =(t -1)%(n +1), y = x % n, z;if(y - l ==-1)printf("%d ", n);else{if(y - l >=0) z = y - l;else z = y - l + n +1;if(a[z]==2&&(e[z]==-1|| p[e[z]]> t ||(p[e[y - l]]== t && e[y - l]> y)))printf("%d ", n +1);elseprintf("%d ", n);}}}printf("
");}return0;}