CF1553I Stairs
说实话网上的题解真的少,这个算是当前最详细的吧。
请原谅我参考了别人的代码,还跑得巨慢的这一事。不过还是 O ( n 2 ) O(n^2) O(n2) 过去了,嘻嘻。
有两个思路,是将贡献不同时间计算的结果。
首先一个合法的序列,肯定是将数值连续的一段拿出来,然后可以分成若干段等长的。这个一开始判断一下即可。
之后我们将分段拿出来不妨将其记录为 s s s 数组。
一个合法的序列就是不能有任意两个 s s s 构成单调序列。
但是这个需要复杂度较高的 d p dp dp,我们考虑通过容斥改成钦定有几个数不合法。
对于一个长度 > 1 > 1 >1 的段,其有两种单调性。长度为 1 1 1 的段,其只有一种单调性,但是向后转移的时候会产生两种单调性。
C a s e 1 \tt Case\ 1 Case 1
我们将单调性在这个序列转移的时候就计算贡献。
设 f ( i , j , 0 / 1 ) f(i, j, 0/1) f(i,j,0/1) 表示当前考虑到第 i i i 个序列,已经钦定了 j j j 个连续,当前段长度是否是 1 1 1。注意这个段是表示已经拼接之后的段。
f ( i − 1 , j , 0 ) → f ( i , j + 1 , 1 ) f ( i − 1 , j , 1 ) × 2 → f ( i , j + 1 , 1 ) f ( i − 1 , j , 0 ) × 2 → f ( i , j , 0 / 1 ) f ( i − 1 , j , 1 ) → f ( i , j , 0 / 1 ) \begin{aligned} & f(i - 1, j, 0) \to f(i, j + 1, 1) \\ & f(i - 1, j, 1) \times 2 \to f(i, j + 1, 1) \\ & f(i - 1, j, 0) \times 2 \to f(i, j, 0/1) \\ & f(i - 1, j, 1) \to f(i, j, 0/1) \end{aligned} f(i−1,j,0)→f(i,j+1,1)f(i−1,j,1)×2→f(i,j+1,1)f(i−1,j,0)×2→f(i,j,0/1)f(i−1,j,1)→f(i,j,0/1)
前面两个转移表示向后拼接,如果长度不是 1 1 1 肯定有两种贡献,如果长度是 1 1 1,那么我们钦定长度为 1 1 1 的贡献肯定是只有一种的,因为我们向后拼接成长度 > 1 > 1 >1 的贡献是需要向后转移的时候算的。
对于不钦定的时候,我们转移的接口在长度 = 1 = 1 =1 的时候肯定是有两种方案的,然后如果是 > 1 > 1 >1 的话,我们要延后贡献所以需要先不计算。
最后计算答案的时候使用容斥即可。
注意:
这里顺便说一下我们在长度是 1 1 1 向后继承的时候贡献 × 2 \times 2 ×2 了,这是和后面的最不一样的地方。
这里也导致常数略大,没有卡过去。
#include <bits/stdc++.h>
using namespace std;
#define Fread
#define Getmod
#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread
template <typename T>
void r1(T &x) {
x = 0;
char

最低0.47元/天 解锁文章
277

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



