JZOJ 6847. 【2020.11.03提高组模拟】通往强者之路(找规律+转化题意)

JZOJ 6847. 【2020.11.03提高组模拟】通往强者之路

题目大意

  • 初始编号为 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,q105 q i ≤ 1 0 18 q_ileq10^{18} qi1018 a i ∈ { n − 1 , n , n + 1 } a_iin{n-1,n,n+1} ai{n1,n,n+1}

题解

  • 找规律或归纳法证明可以得到每个格子的答案都只有 { n − 1 , n , n + 1 } {n-1,n,n+1} {n1,n,n+1}三种情况,不妨把它们分别看作是 { 0 , 1 , 2 } {0,1,2} {0,1,2}
  • 可以每 n n n个格子分成一段,若前 i − 1 i-1 i1段的答案已经计算出来了,考虑如何计算第 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 fi1,j1,那么 f i , j f_{i,j} fi,j可以加 1 1 1;若 f i − 1 , j − 1 = 2 f_{i-1,j-1}=2 fi1,j1=2,那么 f i , j f_{i,j} fi,j可以加 1 1 1。特别地,若 f i − 2 , n − 1 = 2 f_{i-2,n-1}=2 fi2,n1=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段。
  • 那么直接扫一遍求出每个 0 0 0的位置能不能被填上以及什么时候被填上,每个 2 2 2会不会掉落以及什么时候掉落、掉落在哪个位置,最后询问直接 O ( 1 ) O(1) O(1)处理。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define ll long long
int p[N], e[N], a[N], q[N];
int main() {
	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;
			else if(!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);
					else printf("%d ", n);
				}
			} 
		}
		printf("
");
	}
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/14279515.html