Description
先水了发翻译
你现在有一个序列 \(a\),定义一个用该序列生成新序列 \(b\) 的规则如下:
-
把 \(a\) 这个序列分成连续的几段;
-
对于每一段,我们把这一段的长度插入到这一段的右边或左边。
-
每一段进行操作后便得到了 \(b\) 序列。
比如 \(a\) = \([1,2,3,1,2,3]\) ,我们可以把其分成 \(\color {red}[1]\),\(\color {blue}[2,3,1]\),\(\color {green}[2,3]\),长度分别为 \(1\),\(3\),\(2\)。
然后我们把长度随意插入原序列中,可以得到一个不唯一的 \(b\) 序列,例如:\(b = [\color {black}{1,}\color {red}1\color {black},3,\color {blue}{2,3,1},\color {black}2,\color {green}{3,2}]\)。
现在给定一个长度为 \(n\) \((1≤n≤2 \times 10^5)\) 已经操作后的序列 \(b\) \((1\le b_i \le 10^9)\),询问你是否能构造出任意一个原序列 \(a\),使得它进行如上操作后可以得到 \(b\),若能构造出输出 YES
,否则输出 NO
。
共有 \(t\) \((1 \le t \le 10^4)\) 组数据,\(\sum n \le 2 \times 10^5\)。
请注意常数可能造成的影响,否则可能会出现 TLE 等评测结果。
Solution
发现直接扫不好得到答案,同时题目只询问你是否能构造出,可以考虑 DP。
设 f[i][0/1]
表示已经考虑到了第 \(i\) 个位置,这个位置必为某一段的长度,放在开头/末尾否可行。
考虑对两种情况分别进行转移。以下例子中,()
表示现在 \(i\) 的位置
-
考虑这一位必为某段开头的情况:
-
如果它前面那一段也是长度在开头位置,例如 \([\color {black}3,\color {blue}{2,3,1},\color {black}(2) ....]\) ,发现当我们之前计算到 \(3\) 时,就已经可以转移出这个状态的可行性,这个状态就不用考虑了;
-
如果它前面那一段是长度在末尾位置,例如 \([\color {blue}{2,3,1},\color {black}3,(2) ....]\) ,则我们只需要判断 \(i - 1\) 的位置必为末尾这个状态是否可行即可。
-
如果这个状态 f[i][0] = 1
,我们可以去考虑它能向后更新哪个状态(其实就是第一种情况的转移,只不过是某个状态向后转移得到的)。
既然这是长度,说明后面 \([i + 1, i + b_i]\) 这一段都归它了,下一个段只能从 \(i + b_i + 1\) 开始,若就拿 \(b_{i+b_i+1}\) 为某段开头,可以得出 f[i+b[i]+1][0] = 1
\((i + b[i] + 1 \le n + 1)\)。
-
考虑这一位必为某段末尾的情况:
-
如果它前面那一段是长度在开头位置可行,例如 \([\color {black}3,\color {blue}{2,3,1},\color {green}{3,2}, \color {black} {(2)} .... ]\),则根据上面转移可知,以 \(i - a[i]\) 位置为开头的状态为开头应当也是可行的(在这个例子中就是 \(\color {green} 3\) 为开头的状态可行),直接判断
f[i - a[i]][0]
的可行性即可。 -
如果它前面那一段是长度在末尾位置可行,例如 \([\color {blue}{2,3,1},\color {black}3,\color {green}{3,2}, \color {black} {(2)} .... ]\),则根据上面转移同理可知,也是直接判断
f[i - a[i]][0]
的可行性即可。
-
所以这个大情况可以合并为判断 f[i - a[i]][0]
的可行性,即 f[i][1] = f[i - a[i]][0]
\((i - a[i] \ge 1)\)。
考虑获取最终答案,如果最后一段是开头位置为长度,那么可知 f[n+1][0] = 1
;如果最后一段是末尾位置为长度,可知 f[n][1] = 1
所以最后判断这两个状态是否有至少一个可行即可。
关于初值设置,第一个数为开头状态肯定可行,即 f[1][0] = 1
,同时也可知道 f[1 + a[i] + 1][0] = 1
。
由于数据组数过多,不能使用 memset 来清空数组,且需要注意 \(n + 1\) 的 DP 数组也需要清空。
Code
思路中的细节详见代码。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
#define REP(i, x, y) for(int i = x; i < y; i++)
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define PER(i, x, y) for(int i = x; i > y; i--)
#define per(i, x, y) for(int i = x; i >= y; i--)
#define lc (k << 1)
#define rc (k << 1 | 1)
using namespace std;
const int N = 200005;
int T;
int n, a[N], f[N][3];
int main()
{
cin >> T;
while(T--)
{
cin>>n;
rep(i,1,n)
{
cin >> a[i];
f[i][1] = f[i][0] = 0;
}
f[n + 1][1] = f[n + 1][0] = 0;// n + 1 位置也需要清空
f[1][0] = 1;
if(a[1] + 2 <= n + 1)
f[a[1] + 2][0]=1;//初值
for(int i = 2; i <= n; i++)
{
//这一位必为某段开头的情况的转移
if(f[i-1][1] == 1) f[i][0] = 1;
if(f[i][0] == 1)
{
if(i + a[i] + 1 <= n + 1)//注意边界
f[i + a[i] + 1][0] = 1;
}
//这一位必为某段末尾的情况的转移
if(i - a[i] >= 1 && f[i - a[i]][0] == 1)//同样,注意边界
{
f[i][1] = 1;
}
}
if(f[n][1] == 1 || f[n + 1][0] == 1)
{
puts("YES");
continue;
}
else
{
puts("NO");
}
}
return 0;
}
标签:Sending,color,Over,CF1741E,black,开头,一段,长度,include
From: https://www.cnblogs.com/pjxpjx/p/16807756.html