CF2115B Gellyfish and Camellia Japonica
题目大意
对与一个长度为 \(n\) 的数组 \(c\)(我们称数组中第 \(i\) 个数为 \(c_i\)),要进行 \(q\) 次修改操作(第 \(i\) 次修改,由三个整数 \(x_i, y_i, z_i\) 描述,将 \(c_{z_i}\) 修改为 \(\min(c_{x_i},c_{y_i})\)),问:给出操作后的数组以及 \(q\) 次操作,还原可能的数组,若不能还原,输出 -1。
分析
看到题目,\(n \leq 3 \times 10^5\),猜测是 \(O(n)\) 的时间复杂度。
然后,我们容易发现,正着做很难实现,我们尝试倒过来(这是一个经典套路)。
在继续看题,我们可以知道 \(c_{z_i} \leq c_{x_i}\) 同时 \(c_{z_i} \leq c_{y_i}\),假设我们倒着做,容易想到 \(c_{x_i}\) 和 \(c_{y_i}\) 最少也只能是 \(c_{z_i}\)。根据这个,我们就可以倒着覆盖它,令 \(c_{x_i} = \max(c_{x_i}, c_{z_i})\),\(c_{y_i}\) 同理。
至于能否合法,发现不能在计算过程中得出,我们就暴力正着对答案操作,看结果是否与输入数组相同,不同则无解。
此时,你可以过样例了,但是会答案错误!为什么呢?因为在 \(z_i\) 是 \(x_i\) 或 \(y_i\) 的情况下,你是不可以直接确定它的答案的,它可能会与后面某些操作取最大值,导致答案错误。解决这个问题,我们可以直接将它赋值为 \(0\)。这样若造成该种情况也可以复原,同时不影响答案。
最后简单实现一下就好了!
差点场切了,但是因为上面的情况没考虑到,少了两句话,然后答案错误了。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 7;
int n, q, b[MAXN], c[MAXN], d[MAXN];
struct node {
int x, y, z;
} g[MAXN];
signed main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n, &q);
for(int i = 1; i <= n; i++)
{
scanf("%d", &b[i]);
c[i] = b[i];
}
int flag = 0;
for(int i = 1; i <= q; i++)
scanf("%d %d %d", &g[i].x, &g[i].y, &g[i].z);
for(int i = q; i >= 1; i--)
{
int val = c[g[i].z];
c[g[i].z] = 0;
c[g[i].x] = max(c[g[i].x], val);
c[g[i].y] = max(c[g[i].y], val);
// 错误的打法(我赛时没考虑的)
// c[g[i].x] = max(c[g[i].x], c[g[i].z]);
// c[g[i].y] = max(c[g[i].y], c[g[i].z]);
}
for(int i = 1; i <= n; i++)
d[i] = c[i];
for(int i = 1; i <= q; i++)
d[g[i].z] = min(d[g[i].x], d[g[i].y]);
for(int i = 1; i <= n; i++)
{
if(d[i] != b[i])
{
flag = 1;
break;
}
}
if(flag)
{
puts("-1");
continue ;
}
for(int i = 1; i <= n; i++)
printf("%d ",c[i]);
puts("");
}
return 0;
}

浙公网安备 33010602011771号