0%

题目大意

问题描述

给定长度为 n 的数组 a 和整数 k。需要执行 n−k+1 次操作:

  • 每次操作选择一个长度为 k 的窗口,窗口的 MEX 值在所有大小为 k 的窗口中最大

  • 从这个窗口中删除任意一个元素

  • 重复以上过程直到数组长度变为 k−1

目标: 最大化最终剩余元素的 MEX 值

关键概念

MEX (最小排除值): 集合中未出现的最小非负整数

例如: MEX([1,2,0,1,3,0]) 中窗口 [1,2,0] 的 MEX 是 3

输入输出

输入:

  • 第一行: 测试用例数量 t (1≤t≤10⁴)

  • 每个测试用例第一行: n 和 k (2kn2×102≤k≤n≤2×10⁵)

  • 每个测试用例第二行: n 个整数 a,a,...,aa₁,a₂,...,aₙ (0≤aᵢ≤n)

输出: 一个非负整数,表示可能的最大 MEX 值

思路讲解

哈哈,当你发现一道理应很简单的题目有这个比较复杂的决策的时候,这个时候需要想的是,这个决策是不是根本无关紧要?

哈哈,为什么决策根本无关紧要呢?因为答案肯定是 k1≤k-1 的,因此我们需要的数字是 0k20~k-2,不过我们选择的区间长度是 kk,因此如果区间里面数字全是 [0,k2][0,k-2],那必然有重复数字,删除重复的自然是不会对答案产生影响。如果有数字超过这个范围,那删除也更加没有影响

因此其实最后的这个结果就是原数组 mex 值和 k1k-1 中的最小值,

AC代码

AC
https://codeforces.com/contest/2183/submission/358042015

心路历程(WA,TLE,MLE……)

题目大意

这道题是 Alice 和 Bob 的博弈游戏题。

游戏规则:

  • 两人在一个长度为 n 的数组上进行游戏,数组只包含 0 和 1

  • Alice 先手,双方轮流操作

  • 每次操作选择区间 [l, r],将该区间替换为单个数字 1min(al,...,ar)1 - min(a_l, ..., a_r)

  • 如果区间全是 1,则替换为 0;否则替换为 1

  • 游戏持续到数组只剩一个数字为止

获胜条件:

  • 如果最终剩下的数字是 0,Alice 获胜

  • 否则 Bob 获胜

输入格式:

  • 第一行:测试用例数 t (1 ≤ t ≤ 100)

  • 每个测试用例第一行:数组长度 n (3 ≤ n ≤ 100)

  • 每个测试用例第二行:n 个整数 a_i (0 ≤ a_i ≤ 1)

输出格式:

  • 对于每个测试用例,输出 “Alice” 或 “Bob”(大小写不敏感)

要求: 判断在双方最优策略下谁会获胜

思路讲解

这种题目一般是这样的,看起来这个操作非常复杂,但是实际上是只需要很少轮次的最优操作,就可以得出答案。`

首先,如果整个序列都是11,Alice 就可以通过对整个序列进行操作立即获胜。

注意最后一次操作必须涉及至少一个a1a_1ana_n。我们根据a1a_1ana_n的值进行讨论:

如果a1=1a_1 = 1,那么Alice 可以直接在区间[2,n][2,n]上操作以获胜,因为a2na_{2 \sim n}必须包含至少一个00

如果an=1a_n = 1,那么Alice 可以直接在区间[1,n1][1,n-1]上操作以获胜,因为a1n1a_{1 \sim n-1}必须包含至少一个00

如果a1=0a_1 = 0an=0a_n = 0,那么在整个序列上进行操作肯定会导致Alice 失败。这意味着Alice 不能同时操作a1a_1ana_n。因此,在Alice 操作后,序列aa中仍然至少会有一个00。Bob 然后可以在整个序列上操作以获胜。因此,在这种情况下,Bob 获胜。(这么讲有点奇怪,其实就是,你无论第一次你怎么操作,第二次 Bob 都可以直接掀桌子,得到 1

时间复杂度:O(n)O(\sum n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Solve() {
ll N;
cin >> N;
vector<ll> A(N);
for (int i=0;i<N;++i) {
cin>>A[i];
}
// 全 1 的情况
if (find(all(A),0)==A.end()) {
cout<<"Alice\n";
return;
}
// 只要首尾有一个是 1
if (A.front() || A.back()) {
cout<<"Alice\n";
return;
}
// 首尾都为 0
cout<<"Bob\n";
}

AC代码

AC

https://codeforces.com/contest/2183/submission/358031419

心路历程(WA,TLE,MLE……)

题目大意

有 n 个块,编号从 1 到 n,每个块上有一个初始整数 a_i。

对于一个连续段 [l, r],如果整数 d (0 ≤ d ≤ r - l) 满足 min(al,al+1,...,al+d)=dmin(a_l, a_{l+1}, ..., a_{l+d}) = d,则称 d 为 nasty 数字。

一个段的 nastiness 定义为该段中 nasty 数字的数量。

需要处理 q 个操作:

  • **操作 1:**将第 i 个块上的数字修改为 x

  • **操作 2:**查询段 [l, r] 的 nastiness

对于每个操作 2,输出对应段的 nastiness 值。

思路讲解

我们发现一件事情,令 $mn\gets min(a_l, a_{l+1}, …, a_{l+d}) d\uparrowmn\downarrow$,他们两个的这个单调关系是相反的,我们却要求的是 mn=dmn=d 的这个情况的数量,由于 dd 是严格递增的,mnmn 是非严格递减的,这两者显然最多只有一个交点。

那么现在这个问题就来到了,我们实际上就是求一个区间中是否有这个 dd 值嘛,显然,这个可以使用二分解决,只要我们有一个数据结构可以支持这个 RMQ 和这个单点修改即可,可以使用我们的树状数组。

AC代码

AC
https://codeforces.com/contest/2184/submission/358028437

心路历程(WA,TLE,MLE……)

题目大意

给定一棵有 n 个顶点的有根树,根节点为 1。每个叶子节点上都有一个樱桃。

你需要通过"摇动"顶点来收集所有樱桃。摇动顶点 v 时,所有作为 v 后代的叶子节点上的樱桃都会掉落。每个叶子的樱桃只能掉落一次,否则树会损坏。

关键限制:摇动的顶点数量必须是 3 的倍数

问题:是否可能在满足上述条件下收集所有樱桃?

输入格式:

  • 第一行:测试用例数 t (1 ≤ t ≤ 10⁴)

  • 每个测试用例:第一行为 n (2 ≤ n ≤ 2×10⁵),接下来 n-1 行描述树的边

**输出格式:**对每个测试用例输出 “YES” 或 “NO”

思路讲解

当你发现一个问题的子结构之间互相重叠覆盖,其中还涉及这个决策的时候,这个时候,你就要想,是不是 dp 了。

树上 dp,定义状态为 dpu,idp_{u,i},指的是以 uu 为根的子树中,是否可以凑出来为这个 i (0,1,2)i\ (0,1,2) 的这个 3 的余数。

接着问题就来到了如何转移?我们不妨定义,toto 为转移过来的子节点,四个你可以理解为 bitset 的东西,长度为 3,bibi 为目前的状态,若 dpto,0dp_{to,0} 为 true,那么 bi1bi1 就是这个 bibi 向前循环移动的结果,否则 bi1bi1 为全 0。同理,我们可以定义出 bi0bi0bi2bi2。最终经过这个 toto 节点的转移后,bibi0bi1bi2bi\gets bi0 \lor bi1 \lor bi2。(当然,第一个 toto 节点转移过来的话,为了初值确定,bidptobi\gets dp_{to}

最后,令 bi1truebi_1\gets \text{true}(因为我们始终可以选择子树根节点,来解决这个问题),接着 dpubidp_u\gets bi

为什么要这么转移?我们会发现,就是说如果子树只有一个唯一的位移的话,是不是只是让这个状态进行一个位移?那么其实只有一个位移是没什么大用的,但是如果有两种位移?三种位移?这几种位移之间如何组合?那么答案就昭然若揭了。

AC代码

AC
https://codeforces.com/contest/2184/submission/357994744

心路历程(WA,TLE,MLE……)

image

题目大意

给定一个长度为 n 的排列 p。定义一个数组为 k-exquisite 当且仅当:

  • 该数组至少包含两个元素

  • 任意两个相邻元素的差的绝对值至少为 k

对于每个 k(从 1 到 n-1),需要求出排列 p 中有多少个子数组是 k-exquisite 的。

输入格式:

  • 第一行:测试用例数 t(1 ≤ t ≤ 25000)

  • 每个测试用例:

    • 第一行:整数 n,排列的长度(2 ≤ n ≤ 10⁵)
    • 第二行:n 个整数 p₁, p₂, …, pₙ,表示排列(1 ≤ pᵢ ≤ n,且各不相同)

输出格式:

对于每个测试用例,输出 n-1 个整数,第 i 个整数表示 i-exquisite 子数组的个数。

数据范围:

  • 时间限制:2 秒

  • 内存限制:256 MB

  • 所有测试用例的 n 之和不超过 2×10⁵

思路讲解

感觉比 D,B 简单。

如果只需要求这个 k-exquisite 子数组的个数,那就直接双指针,不过这波是要求这个 i-exquisite 子数组的个数。

我们想到,其实这个无非就是把整个数组切成了几段,我们可以采用做减法的方式,去解决这个问题。具体来讲,设我们所求的这个子数组参数为 kk,一个差值 Δ\DeltakΔk≤\Delta 可以直接跨过去,k>Δk>\Delta 就不可以。

image

不难想到,我们可以对这个差值进行排序,或者一种更方便的实现是使用这个 map,并在 set 中存放分隔点,初始设置为这个 0,N(其实这里是一个坑点,不要选成 0,N+1 了,因为我们的分隔点采用前向点,最大为 N-1,因此哨兵应该选 N)。不难发现,设我们的新分隔点为 pospos,set 数组中第一个比我们小的数字是 lowlow,第一个比我们大的数字是 highhigh,我们所需要减去的值就是 (poslow)×(highpos)(pos-low)\times(high-pos),即 low 区有 (poslow)(pos-low) 种选择,high 区是 (highpos)(high-pos) 种选择,使用乘法原理乘起来即可。

1
2
3
4
5
6
7
8
9
10
11
set<ll> st={0,N};
for (int i=1;i<=N-1;++i) {
cout<<ans<<" ";
for (auto pos:mp_diff_pos[i]) {
ll low=get_lower(pos);
ll high=*st.lower_bound(pos);
ll minus=(pos-low)*(high-pos);
ans-=minus;
st.insert(pos);
}
}

AC代码

AC
https://codeforces.com/contest/2184/submission/357897043

心路历程(WA,TLE,MLE……)