Address
Solution
-
可以发现,一个欧拉序 a a a 合法当且仅当:
- ∀ 1 ≤ i ≤ 2 n − 1 , 1 ≤ a i ≤ n \forall 1 \le i \le 2n - 1, 1 \le a_i \le n ∀1≤i≤2n−1,1≤ai≤n;
- a 1 = a 2 n − 1 a_1 = a_{2n - 1} a1=a2n−1;
- ∀ a l = a r \forall a_l = a_r ∀al=ar, r − l r - l r−l 为偶数且 [ l , r ] [l,r] [l,r] 内出现了恰好 r − l 2 + 1 \dfrac{r - l}{2} + 1 2r−l+1 种不同的权值;
- 所有满足 a l = a r a_l = a_r al=ar 的区间 [ l , r ] [l,r] [l,r] 只有包含关系。
-
设当前区间为 [ l , r ] [l,r] [l,r],因为满足 a l ′ = a r ′ a_l' = a_r' al′=ar′ 的区间 [ l ′ , r ′ ] [l',r'] [l′,r′] 一定代表一棵子树,可以递归处理 ( l ′ , r ′ ) (l',r') (l′,r′),然后将 [ l ′ , r ′ ] [l',r'] [l′,r′] 缩成一个点 a l ′ a_{l'} al′。
-
对于每个 i i i 预处理下一个和它权值相同的位置,就可以在缩点时顺便判断一下条件 4 4 4。
-
现在 [ l , r ] [l,r] [l,r] 中就不存在权值相同的点,如果区间中出现了少于 r − l 2 + 1 \frac{r - l}{2} + 1 2r−l+1 种权值,就将还未出现的权值填在区间的最前面(若权值种类数已经超过 r − l 2 + 1 \frac{r - l}{2} + 1 2r−l+1 则无解), 之后从左往右考虑 [ l , r ] [l,r] [l,r] 中每个长度为 3 3 3 的区间,若存在一个长度为 3 3 3 的区间为
xy0或0yx的形式,我们就可以将其补成xyx,然后再缩成一个点x。 -
将所有能够缩的区间缩完之后可能还会有位置为 0 0 0,我们可以将剩下的位置都填成这棵子树的根。
-
考虑这样做的正确性:
- 若不存在两个不为 0 0 0 的位置相邻,区间的首尾一定已经填入数字且相邻的两个已经填入数字的位置中恰好隔着一个 0 0 0,此时将剩下的位置填成这棵子树的根显然正确;
- 若存在两个不为 0 0 0 的位置相邻,缩点之后不为 0 0 0 的位置减少了 1 1 1,为 0 0 0 的位置也减少了 1 1 1,两者的差不变,因此可以看做是递归下去,直到出现第 1 1 1 种情况。
-
特别地,对于 [ 1 , 2 n − 1 ] [1, 2n - 1] [1,2n−1] 我们需要保证首尾相同,进行一些简单的讨论:
- 若 a 1 , a 2 n − 1 > 0 a_1, a_{2n - 1} > 0 a1,a2n−1>0 且 a 1 ≠ a 2 n − 1 a_1 \neq a_{2n - 1} a1=a2n−1,无解;
- 若 a 1 , a 2 n − 1 > 0 a_1, a_{2n - 1} > 0 a1,a2n−1>0 且 a 1 = a 2 n − 1 a_1 = a_{2n - 1} a1=a2n−1,递归区间 ( 1 , 2 n − 1 ) (1, 2n - 1) (1,2n−1);
- 若 a 1 > 0 a_1 > 0 a1>0 或 a 2 n − 1 > 0 a_{2n - 1} > 0 a2n−1>0,将两个数填成相同的,递归区间 ( 1 , 2 n − 1 ) (1, 2n - 1) (1,2n−1);
- 若 a 1 = a 2 n − 1 = 0 a_1 = a_{2n - 1} = 0 a1=a2n−1=0,直接按之前算法处理即可,从左到右合并能保证最后首尾相同且没有剩余位置为 0 0 0。
-
可以用链表实现,时间复杂度 O ( n ) \mathcal O(n) O(n),期望得分 100pts \text{100pts} 100pts。
Code
#include <bits/stdc++.h>
template <class T>
inline void read(T &res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
}
template <class T>
inline void put(T x)
{
if (x > 9)
put(x / 10);
putchar(x % 10 + 48);
}
const int N = 1e6 + 5;
int a[N], vis[N], nxt[N], pre[N], suf[N];
int n, m, now = 1;
inline void Fail()
{
puts("no");
exit(0);
}
inline int Get()
{
while (vis[now])
++now;
if (now > n)
Fail();
vis[now] = 1;
return now;
}
inline void Delete(int l, int r)
{
int tl = pre[l], tr = suf[r];
suf[tl] = tr, pre[tr] = tl;
}
inline void Merge(int l, int r, int &x)
{
while (x > l && suf[suf[x]] && suf[suf[x]] <= r && !a[x] && a[suf[x]] && a[suf[suf[x]]])
{
a[x] = a[suf[suf[x]]];
Delete(suf[x], suf[suf[x]]);
x = pre[pre[x]];
}
while (x > l && suf[suf[x]] && suf[suf[x]] <= r && a[x] && a[suf[x]] && !a[suf[suf[x]]])
{
a[suf[suf[x]]] = a[x];
Delete(suf[x], suf[suf[x]]);
x = pre[pre[x]];
}
}
inline void solve(int l, int r)
{
if (r - l & 1)
Fail();
for (int i = l; i <= r; i = suf[i])
while (nxt[i])
{
if (nxt[i] > r)
Fail();
solve(suf[i], pre[nxt[i]]);
Delete(suf[i], nxt[i]);
nxt[i] = nxt[nxt[i]];
}
int num1 = 0, num2 = 0, rt = a[pre[l]];
for (int i = l; i <= r; i = suf[i])
num1 += a[i] > 0, ++num2;
num2 = (num2 >> 1) + 1;
if (num1 > num2)
Fail();
num2 -= num1;
for (int i = l; num2 && i <= r; i = suf[i])
if (!a[i])
a[i] = Get(), --num2;
for (int i = l; i <= r; i = suf[i])
Merge(l - 1, r, i);
for (int i = l; i <= r; i = suf[i])
if (!a[i])
a[i] = rt;
}
int main()
{
read(n);
if (n == 1)
return puts("yes\n1"), 0;
m = (n << 1) - 1;
for (int i = 1; i <= m; ++i)
read(a[i]);
for (int i = 2; i <= m; ++i)
if (a[i] && a[i - 1] && a[i] == a[i - 1])
Fail();
if (a[1] && a[m] && a[1] != a[m])
Fail();
a[1] = a[m] = a[1] | a[m];
for (int i = 0; i <= m + 1; ++i)
pre[i] = i - 1, suf[i] = i + 1;
pre[0] = 0;
for (int i = m; i >= 1; --i)
if (a[i])
{
nxt[i] = vis[a[i]];
vis[a[i]] = i;
}
solve(1, m);
puts("yes");
for (int i = 1; i <= m; ++i)
put(a[i]), putchar(' ');
return 0;
}
本文详细解析了 CF1053E 题目,介绍了一个欧拉序合法性的判断条件及解决方案。通过预处理和递归处理,确保了区间内的权值满足特定条件,并使用链表实现,最终达到 O(n) 的时间复杂度。
296

被折叠的 条评论
为什么被折叠?



