CF1553I Stairs 题解

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(i1,j,0)f(i,j+1,1)f(i1,j,1)×2f(i,j+1,1)f(i1,j,0)×2f(i,j,0/1)f(i1,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 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值