题目大意:
T组样例,每组样例有一个n个数字的序列,(长度为n的数组),每次可以选择序列中任意一个大于0的数字操作,使其自减1
(a = a - 1)
问最终是否可以形成一个序列,满足对其中某一个位置k而言
当然,如果序列是严格递增或者递减的也是可行的
思路
说实话,题目不难,但是我感觉思路很巧妙,就记录一下吧
首先考虑最简单的情况,递增或者递减,可以先处理出来所有的位置上的数字可否在当前位置使前面(或后面)的数字完成递增(或递减),可以用两个数组c1,c2表示可行否。
如果一个位置递增可行,条件是,首先前面的那个数字递增可行,然后就是当前位置的数字大小要求,因为是严格递增,对于第2个数字而言,要想完成递增前面至少是一个数字0,那么自己就必须大于等于1,同理,递减也是一样
但是递增是从1到n,递减就是从n到1递减了
如果递增到最后一个数字依然可行,说明整个序列可以转换为递增,或者,如果递减到第一个数依然可行,说明整个序列可以转换为递减的。那就一定可行了。
对于中间的正态分布形状的序列而言,可以通过上面的两个数组求解
对于其中某一个位置k,如果c1[k] = true and c2[k] = true,说明当前位置前面可以完成一次严格递增(包含自身),并且后面可以完成一次严格递减(包含自身),说明已经可以形成上述形状的序列了,只需要遍历一边1到n,判断c1[i] 和 c2[i]是否可以同时为true,只要有一个满足即可完成。
不用纠结与a[k]位置的值,满足前面递增后面递减是否会不相等,既然可以完成上述操作,说明可以转换为下述序列的样子
最后全都转换成这样总一定可行了吧
#include <bits/stdc++.h>
using namespace std;
const int N = 300100;
int arr[N];
#define mem(a, b) memset(a, b, sizeof a)
#pragma warning (disable:6031)
#pragma warning (disable:4996)
int t, n;
bool c1[N], c2[N];
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
bool f1 = 1, f2 = 1;
bool f3 = 1;
for (int i = 1; i <= n; i++)scanf("%d", arr + i);
mem(c1, 0);// 初始均不行
mem(c2, 0);
c1[1] = 1;
// c1记录递增
for (int i = 2; i <= n; i++) {
if (c1[i - 1] && arr[i] >= i - 1) {
c1[i] = 1;
// 如果上一个数字可行,并且当前数字大小可行,即为可行
}
else {
break;
// 如果当前数字不行,后面的都不会行
}
}
// 同上
c2[n] = 1;
for (int i = n - 1; i >= 1; i--) {
if (c2[i + 1] && arr[i] >= n - i) {
c2[i] = 1;
}
else break;
}
f1 = c1[n];
f2 = c2[1];
f3 = 0;
for (int i = 1; i <= n; i++) {
if (c1[i] && c2[i]) {
f3 = 1;
}
}
// f1 表示序列可否转为全部递增
// f2 表示序列可否转为全部递减
// f3 表示能否形成正态分布形状的序列
if (f1 || f2 || f3)puts("Yes");
else puts("No");
}
return 0;
}