CF1741E Sending a Sequence Over the Network
Description
先水了发翻译
你现在有一个序列 ,定义一个用该序列生成新序列 的规则如下:
-
把 这个序列分成连续的几段;
-
对于每一段,我们把这一段的长度插入到这一段的右边或左边。
-
每一段进行操作后便得到了 序列。
比如 = ,我们可以把其分成 ,,,长度分别为 ,,。
然后我们把长度随意插入原序列中,可以得到一个不唯一的 序列,例如:。
现在给定一个长度为 已经操作后的序列 ,询问你是否能构造出任意一个原序列 ,使得它进行如上操作后可以得到 ,若能构造出输出 YES
,否则输出 NO
。
共有 组数据,。
请注意常数可能造成的影响,否则可能会出现 TLE 等评测结果。
Solution
发现直接扫不好得到答案,同时题目只询问你是否能构造出,可以考虑 DP。
设 f[i][0/1]
表示已经考虑到了第 个位置,这个位置必为某一段的长度,放在开头/末尾否可行。
考虑对两种情况分别进行转移。以下例子中,()
表示现在 的位置
-
考虑这一位必为某段开头的情况:
-
如果它前面那一段也是长度在开头位置,例如 ,发现当我们之前计算到 时,就已经可以转移出这个状态的可行性,这个状态就不用考虑了;
-
如果它前面那一段是长度在末尾位置,例如 ,则我们只需要判断 的位置必为末尾这个状态是否可行即可。
-
如果这个状态 f[i][0] = 1
,我们可以去考虑它能向后更新哪个状态(其实就是第一种情况的转移,只不过是某个状态向后转移得到的)。
既然这是长度,说明后面 这一段都归它了,下一个段只能从 开始,若就拿 为某段开头,可以得出 f[i+b[i]+1][0] = 1
。
-
考虑这一位必为某段末尾的情况:
-
如果它前面那一段是长度在开头位置可行,例如 ,则根据上面转移可知,以 位置为开头的状态为开头应当也是可行的(在这个例子中就是 为开头的状态可行),直接判断
f[i - a[i]][0]
的可行性即可。 -
如果它前面那一段是长度在末尾位置可行,例如 ,则根据上面转移同理可知,也是直接判断
f[i - a[i]][0]
的可行性即可。
-
所以这个大情况可以合并为判断 f[i - a[i]][0]
的可行性,即 f[i][1] = f[i - a[i]][0]
。
考虑获取最终答案,如果最后一段是开头位置为长度,那么可知 f[n+1][0] = 1
;如果最后一段是末尾位置为长度,可知 f[n][1] = 1
所以最后判断这两个状态是否有至少一个可行即可。
关于初值设置,第一个数为开头状态肯定可行,即 f[1][0] = 1
,同时也可知道 f[1 + a[i] + 1][0] = 1
。
由于数据组数过多,不能使用 memset 来清空数组,且需要注意 的 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】