CF1741E Sending a Sequence Over the Network

Description

先水了发翻译

你现在有一个序列 a,定义一个用该序列生成新序列 b 的规则如下:

  • a 这个序列分成连续的几段;

  • 对于每一段,我们把这一段的长度插入到这一段的右边左边。

  • 每一段进行操作后便得到了 b 序列。

比如 a = [1,2,3,1,2,3] ,我们可以把其分成 [1][2,3,1][2,3],长度分别为 132

然后我们把长度随意插入原序列中,可以得到一个不唯一的 b 序列,例如:b=[1,1,3,2,3,1,2,3,2]

现在给定一个长度为 n (1n2×105) 已经操作后的序列 b (1bi109),询问你是否能构造出任意一个原序列 a,使得它进行如上操作后可以得到 b,若能构造出输出 YES,否则输出 NO

共有 t (1t104) 组数据,n2×105

请注意常数可能造成的影响,否则可能会出现 TLE 等评测结果。

Solution

发现直接扫不好得到答案,同时题目只询问你是否能构造出,可以考虑 DP。

f[i][0/1] 表示已经考虑到了第 i 个位置,这个位置必为某一段的长度,放在开头/末尾否可行。

考虑对两种情况分别进行转移。以下例子中,() 表示现在 i 的位置

  • 考虑这一位必为某段开头的情况:

    • 如果它前面那一段也是长度在开头位置,例如 [3,2,3,1,(2)....] ,发现当我们之前计算到 3 时,就已经可以转移出这个状态的可行性,这个状态就不用考虑了;

    • 如果它前面那一段是长度在末尾位置,例如 [2,3,1,3,(2)....] ,则我们只需要判断 i1 的位置必为末尾这个状态是否可行即可。

如果这个状态 f[i][0] = 1 ,我们可以去考虑它能向后更新哪个状态(其实就是第一种情况的转移,只不过是某个状态向后转移得到的)。

既然这是长度,说明后面 [i+1,i+bi] 这一段都归它了,下一个段只能从 i+bi+1 开始,若就拿 bi+bi+1 为某段开头,可以得出 f[i+b[i]+1][0] = 1 (i+b[i]+1n+1)

  • 考虑这一位必为某段末尾的情况:

    • 如果它前面那一段是长度在开头位置可行,例如 [3,2,3,1,3,2,(2)....],则根据上面转移可知,以 ia[i] 位置为开头的状态为开头应当也是可行的(在这个例子中就是 3 为开头的状态可行),直接判断 f[i - a[i]][0] 的可行性即可。

    • 如果它前面那一段是长度在末尾位置可行,例如 [2,3,1,3,3,2,(2)....],则根据上面转移同理可知,也是直接判断 f[i - a[i]][0] 的可行性即可。

所以这个大情况可以合并为判断 f[i - a[i]][0] 的可行性,即 f[i][1] = f[i - a[i]][0] (ia[i]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;
}

posted @   panjx  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示