[学习笔记] 关于 LIS/LDS 的一些奇奇怪怪的东西~

本文介绍了如何利用树状数组、线段树和Dilworth定理解决极长LIS问题,包括基本方法、方案数维护、树上LIS实例以及处理斜率作为权值的情况。通过实例演示了如何在BZOJ和Nowcoder竞赛题中应用这些技巧,探讨了从固定权值到极长LIS的转化和优化策略。

推销我的博客园~

球球大家了来看看我的博客园吧,阅读量 0 0 0 是人干事?

最基础的方法

有用的信息无非是 “权值” 和 “长度”。于是可以固定权值,求最长长度(树状数组);或者固定长度 j j j,求最小权值 g j g_j gj

方案数?

延续上文的方法:可以用树状数组维护最值时同时维护方案数;或者,将 g g g 开成一个 vector \text{vector} vector,放置所有 LIS \text{LIS} LIS j j j 的数字及其方案数,显然这些数字是递减的。设位置 i i i 的方案数为 f i f_i fi,它的 LIS \text{LIS} LIS p p p,显然有如下转移:

KaTeX parse error: Undefined control sequence: \and at position 23: …um_{j\in g(p-1)\̲a̲n̲d̲ ̲a_j<a_i} f_j

因为存储了历史值,所以显然不是所有属于 g ( p − 1 ) g(p-1) g(p1) 的数字都能转移到 i i i。所以需要做一个前缀和 + + + 二分。

Dilworth \text{Dilworth} Dilworth 定理

不下降子序列最小个数 = = = LIS \text{LIS} LIS 的长度。

树上 LIS \text{LIS} LIS

例 1. BZOJ  \text{BZOJ } BZOJ 大根堆

还是和 基础方法 一样:对于固定权值,此时发现儿子之间是互不影响的,于是可以使用线段树合并;对于固定长度,定长的数组无法处理,所以可以开一个 multiset \text{multiset} multiset

例 2. CF490F Treeland Tour \text{CF490F Treeland Tour} CF490F Treeland Tour

对于固定权值,用线段树分别维护 LIS \text{LIS} LIS LDS \text{LDS} LDS。具体合并两棵线段树时,可以以 mid \text{mid} mid 为界进行 LIS \text{LIS} LIS LDS \text{LDS} LDS 的合并。

极长 LIS \text{LIS} LIS

例 1. BZOJ - 2957  \text{BZOJ - 2957 } BZOJ - 2957 楼房重建

将斜率作为权值,题目要求的就是 能选则选 的极长 LIS \text{LIS} LIS

考虑用线段树维护,节点 [ l , r ] [l,r] [l,r] 维护区间最大斜率 k k k l l l 开始 的极长 LIS \text{LIS} LIS,令其为 L L L。定义 calc ( o , k ) \textbf{calc}(o,k) calc(o,k) 为区间 o o o 中,且起点大于 k k k 的极长 LIS \text{LIS} LIS

那么更新节点 o o o 的答案时,就只用左儿子的答案加上 calc ( rson , k lson ) \textbf{calc}(\text{rson},k_{\text{lson}}) calc(rson,klson) 即可。

关于 calc ( ) \textbf{calc}() calc() 的内部实现,当左儿子的最大斜率不大于 k k k 时直接递归右儿子;反之递归左儿子,加上右半部分已经算好的值(因为左半部分的斜率不会变化)。注意右半部分算好的值不是 L rson L_{\text{rson}} Lrson,这个没有考虑左半部分的斜率。

时间复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n)

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f |= (s=='-');
	while(s>='0' and s<='9')
		x = (x<<1)+(x<<3)+(s^48),
		s = getchar();
	return f?-x:x;
}

template <class T>
inline void write(T x) {
    static int writ[40],w_tp=0;
    if(x<0) putchar('-'),x=-x;
    do writ[++w_tp]=(x-x/10*10),x/=10; while(x);
    while(putchar(writ[w_tp--]^48),w_tp);
}

#include <iostream>
using namespace std;

const int maxn = 1e5+5;

int n,m;
struct node {
    double k; int ans;
} t[maxn<<2];

int calc(int o,int l,int r,const double k) {
    if(l==r) return t[o].k>k;
    int mid=l+r>>1;
    if(t[o<<1].k<=k) 
        return calc(o<<1|1,mid+1,r,k);
    return calc(o<<1,l,mid,k)+t[o].ans-t[o<<1].ans;
}

void ins(int o,int l,int r,int p,const double k) {
    if(l==r) return t[o].k=k,t[o].ans=1,void();
    int mid=l+r>>1;
    if(p<=mid) ins(o<<1,l,mid,p,k);
    else ins(o<<1|1,mid+1,r,p,k);
    t[o].k = max(t[o<<1].k,t[o<<1|1].k);
    t[o].ans = t[o<<1].ans+calc(o<<1|1,mid+1,r,t[o<<1].k);
}

int main() {
    n=read(9),m=read(9);
    for(int i=1;i<=m;++i) {
        int x=read(9),y=read(9);
        ins(1,1,n,x,1.0*y/x);
        print(t[1].ans,'\n');
    }
    return 0;
}

例 2. Nowcoder - 7615D \text{Nowcoder - 7615D} Nowcoder - 7615D 牛半仙的妹子序列

**题目大意:**给定一个长度为 n n n 的排列,求出包含极长上升子序列的个数。 n ≤ 2 ⋅ 1 0 5 n\le 2\cdot 10^5 n2105

首先想到 d p \mathtt{dp} dp,发现 j j j 能向 i i i 转移的条件是:只考虑 < a i <a_i <ai 的数, a j a_j aj 是区间 [ j , i ) [j,i) [j,i) 中最大的数,且 a j < a i a_j<a_i aj<ai

其实这可以转化为 cdq \text{cdq} cdq 分治,我们可以按照 a a a 来分治,转移时用前半部分贡献后半部分即可。

问题是后面的条件如何满足?事实上它可以转化为双向的条件:在 [ l , mid ] [l,\text{mid}] [l,mid] 之中求出第一个满足在 原序列 上下标大于 j j j 且值大于 a j a_j aj 的数字在原序列上的下标 r j r_j rj;在 ( mid , r ] (\text{mid},r] (mid,r] 之中求出第一个满足在 原序列 上下标小于 i i i 且值小于 a i a_i ai 的数字在原序列上的下标 l i l_i li

由于 r j , l i r_j,l_i rj,li 均满足权值在 ( a j , a i ) (a_j,a_i) (aj,ai) 之间,那么显然上面的条件可以这样被描述:

l i < j < i < r j l_i<j<i<r_j li<j<i<rj

这个问题就很简单了 —— 将 i i i 按照 l i l_i li 从大到小排序,依次在树状数组中添加 j j j d p \mathtt{dp} dp 值,拿 r i r_i ri 来查询即可。

时间复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n)


其实这道题目也可以转化为上一道例题:我们发现,对于每个 i i i,合法的 j j j 其实就是从 i i i 开始能选则选的单增序列(当然从左往右就是单减的)!转移时从小到大枚举权值,这样就能丢掉 最大值小于 a i a_i ai 的限制。维护时直接套板子, calc ( o , k ) \textbf{calc}(o,k) calc(o,k) 的含义就是大于 k k k 的方案数,所以需要维护最大值。时间复杂度仍然是 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n) 的。

看看代码吧:

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f |= (s=='-');
	while(s>='0' and s<='9')
		x = (x<<1)+(x<<3)+(s^48),
		s = getchar();
	return f?-x:x;
}

template <class T>
inline void write(T x) {
    static int writ[40],w_tp=0;
    if(x<0) putchar('-'),x=-x;
    do writ[++w_tp]=(x-x/10*10),x/=10; while(x);
    while(putchar(writ[w_tp--]^48),w_tp);
}

#include <iostream>
using namespace std;

const int maxn = 2e5+5;
const int mod = 998244353;

int n,pos[maxn],lim;
int a[maxn],dp[maxn];
struct node {
    int mx,lans;
} t[maxn<<2];

inline int inc(int x,int y) {
    return x+y>=mod?x+y-mod:x+y;
}

int calc(int o,int l,int r,int k) {
    if(l==r) return t[o].mx>k?dp[t[o].mx]:0;
    int mid=l+r>>1;
    if(t[o<<1|1].mx<=k) return calc(o<<1,l,mid,k);
    return inc(calc(o<<1|1,mid+1,r,k),t[o].lans);
}

int ask(int o,int l,int r,int L,int R) {
    if(l>=L && r<=R) {
        int v=lim; lim = max(lim,t[o].mx);
        return calc(o,l,r,v);
    }
    int mid=l+r>>1,ret=0;
    if(R>mid) ret=ask(o<<1|1,mid+1,r,L,R);
    if(L<=mid) ret=inc(ret,ask(o<<1,l,mid,L,R));
    return ret;
}

void ins(int o,int l,int r,int p,int k) {
    if(l==r) return t[o].mx=k,void();
    int mid=l+r>>1;
    if(p<=mid) ins(o<<1,l,mid,p,k);
    else ins(o<<1|1,mid+1,r,p,k);
    t[o].mx = max(t[o<<1].mx,t[o<<1|1].mx);
    t[o].lans = calc(o<<1,l,mid,t[o<<1|1].mx);
}

int main() {
    n=read(9);
    for(int i=1;i<=n;++i) 
		pos[a[i]=read(9)]=i;
    for(int i=1;i<=n;++i) {
        int wh = pos[i]; lim=0;
        dp[i] = ask(1,1,n,1,wh);
        if(!dp[i]) dp[i]=1;
        ins(1,1,n,wh,i);
    }
    int ans=0; lim=0;
    for(int i=n;i;--i)
    	if(lim<a[i]) {
    		ans = inc(ans,dp[a[i]]);
    		lim = a[i];
		}
	print(ans,'\n');
    return 0;
}
<think>我们面对两个主要问题: 1. Redis报错:MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. 这通常是因为磁盘空间不足或权限问题导致Redis无法将内存中的数据写入磁盘。 2. 在诊断过程中,执行`df -h | grep`命令报错(可能是命令不完整导致),而执行`du -sh /var/lib/redis/*`时提示文件或目录不存在。 根据引用[3],我们首先需要检查磁盘空间,但用户在执行相关命令时遇到问题。因此,我们需要先解决命令执行的问题,然后处理Redis的错误。 步骤分解: 1. **解决命令执行问题**: - `df -h | grep` 报错:可能是因为`grep`后面缺少模式(pattern)导致的。正确的命令应该是`df -h | grep /`(如引用[3]所示)或者根据需求指定挂载点。 - `du -sh /var/lib/redis/*` 提示文件或目录不存在:这可能是因为`/var/lib/redis`目录下没有文件,或者该目录本身不存在。需要检查Redis的数据目录是否正确。 2. **定位Redis数据目录**: - 通过Redis的配置文件(通常是`/etc/redis/redis.conf`)查找`dir`配置项,该配置项指定了RDB文件的存储路径(引用[2])。 - 如果配置的目录不是`/var/lib/redis`,那么使用`du`命令时就应该使用配置的目录。 3. **检查磁盘空间和权限**: - 使用`df -h`查看整个磁盘的使用情况,特别关注Redis数据目录所在的磁盘分区。 - 使用`du -sh <redis_dir>`检查Redis数据目录的大小,并确保该目录存在。 4. **解决Redis错误**: - 如果发现磁盘空间不足(如引用[3]中`/`分区已用100%),需要清理磁盘空间。 - 如果磁盘空间足够,则检查Redis数据目录的权限(引用[4]提到可能因为只读文件系统导致写入失败,需要调整目录权限或修改systemd配置)。 具体步骤: **步骤1:检查磁盘空间(修正命令)** 执行以下命令: ```bash df -h ``` 观察输出,特别是挂载点为`/`的分区(系统根分区)使用情况,以及Redis数据目录所在的分区(如果Redis数据目录在独立分区上)。如果根分区使用率接近100%,则需要清理空间。 **步骤2:确认Redis数据目录** 查找Redis配置的数据目录: ```bash # 如果Redis服务正在运行,可以使用: redis-cli config get dir # 或者查看配置文件 grep '^dir' /etc/redis/redis.conf ``` 假设输出为`/var/lib/redis`,那么我们就需要检查这个目录。 **步骤3:检查数据目录是否存在及权限** 如果上一步确认的目录是`/var/lib/redis`,但执行`du -sh /var/lib/redis/*`报错“文件或目录不存在”,则可能是该目录下没有任何文件(包括隐藏文件)或者目录不存在。 检查目录是否存在: ```bash ls -ld /var/lib/redis ``` 如果目录不存在,则需要创建并设置正确的权限: ```bash sudo mkdir -p /var/lib/redis sudo chown redis:redis /var/lib/redis # 假设Redis服务以redis用户运行 sudo chmod 700 /var/lib/redis ``` **步骤4:检查目录权限** 如果目录存在,检查权限: ```bash ls -ld /var/lib/redis ``` 应该显示为redis用户所有,且具有写权限。如果不是,则修改权限: ```bash sudo chown -R redis:redis /var/lib/redis sudo chmod -R 700 /var/lib/redis ``` **步骤5:检查是否只读文件系统** 如果磁盘空间足够,权限也正确,但Redis仍然无法写入,可能是文件系统被挂载为只读(如引用[4]提到的`Read-only file system`)。检查方法: ```bash mount | grep ' / ' ``` 如果显示`ro`,则需要重新挂载为读写(但注意系统可能因为磁盘错误自动挂载为只读,此时需要修复磁盘错误): ```bash mount -o remount,rw / ``` 如果问题是由于systemd的配置限制(引用[4]),则编辑`/etc/systemd/system/redis.service`,在`[Service]`部分添加或修改: ``` ReadWriteDirectories=/var/lib/redis ``` 然后重启Redis服务: ```bash sudo systemctl daemon-reload sudo systemctl restart redis ``` **步骤6:临时解决方案** 如果需要临时恢复Redis的写操作(在解决根本问题前),可以执行: ```bash redis-cli config set stop-writes-on-bgsave-error no ``` 但请注意,这会允许Redis在持久化失败时继续接受写命令,有数据丢失风险。 **步骤7:清理磁盘空间(如果磁盘空间不足)** 如果步骤1中发现磁盘空间不足(比如根分区满了),需要清理空间。常见方法: - 清理旧的日志文件(如`/var/log`下的日志) - 清理不需要的软件包缓存(如`apt-get clean`或`yum clean all`) - 查找并删除大文件:`sudo find / -xdev -type f -size +100M -print | xargs ls -lh`(注意:在生产环境谨慎操作) - 如果Redis数据目录在根分区上,且根分区空间紧张,可以考虑将Redis数据目录迁移到其他分区(需要修改Redis配置并重启服务)。 **步骤8:验证修复** 重新触发RDB持久化: ```bash redis-cli save # 或者 bgsave ``` 然后检查日志: ```bash tail -f /var/log/redis/redis-server.log ``` 确认没有错误信息。 如果以上步骤不能解决问题,请根据Redis日志(`/var/log/redis/redis-server.log`)中的具体错误信息进一步排查。 注意:在操作过程中,如果遇到命令报错,请根据错误提示调整命令(如权限不足时使用`sudo`)。 下面我们按用户要求的结构化方式呈现解决方案。 </think> ### 解决Redis RDB快照持久化失败及文件系统问题 #### 问题诊断 1. **Redis错误核心原因** - Redis配置了RDB快照但无法写入磁盘(`stop-writes-on-bgsave-error`启用) - 可能原因:**磁盘空间耗尽**(引用[3])、**目录权限错误**(引用[4])或**文件系统只读** 2. **命令报错分析** - `df -h | grep`报错 → grep缺少匹配模式 - `du -sh /var/lib/redis/*`报"文件不存在" → **Redis数据目录配置错误或未初始化** --- ### 解决方案 #### 第一步:修复命令执行环境 ```bash # 1. 修正磁盘检查命令(添加挂载点匹配) df -h | grep -E '/$|/var/lib/redis' # 检查根分区和Redis目录所在分区 # 2. 确认Redis实际数据目录 redis-cli config get dir # 获取运行时配置目录(例如可能返回 /data/redis) # 3. 检查真实目录(假设返回 /data/redis) du -sh /data/redis # 替代原先的错误路径 ``` #### 第二步:解决磁盘空间问题(引用[3]) ```bash # 1. 清理根分区(当df显示100%已用) sudo find /var/log -type f -name "*.log" -regex '.*\.log$' -mtime +30 -delete # 删除30天前日志 sudo journalctl --vacuum-size=500M # 清理系统日志 sudo apt clean # 清理软件包缓存(Debian系) # 2. 若Redis目录独立(如/data),检查其分区 df -h /data # 确认使用率 ``` #### 第三步:修复目录权限与配置(引用[4]) ```bash # 1. 确认Redis服务配置 sudo systemctl cat redis.service | grep ReadWriteDirectories # 2. 修改systemd单元文件(针对只读文件系统错误) sudo vi /etc/systemd/system/redis.service # 在[Service]部分添加: ReadWriteDirectories=/data/redis # 与redis.conf的dir一致 # 3. 重载并重启 sudo systemctl daemon-reload sudo systemctl restart redis ``` #### 第四步:重建Redis数据目录 ```bash # 1. 创建正确目录(根据redis-cli config get dir结果) sudo mkdir -p /data/redis # 示例目录 # 2. 设置权限 sudo chown -R redis:redis /data/redis sudo chmod 770 /data/redis # 3. 更新Redis配置 redis-cli config set dir /data/redis redis-cli config rewrite # 持久化配置到redis.conf ``` #### 第五步:验证修复 ```bash # 1. 触发RDB保存 redis-cli bgsave # 2. 检查日志 sudo tail -n 20 /var/log/redis/redis.log # 期望输出: "Background saving terminated with success" # 3. 确认文件生成 ls -lh /data/redis/dump.rdb # 应有rdb文件 ``` #### 紧急恢复(临时方案) ```bash # 允许写入(数据丢失风险) redis-cli config set stop-writes-on-bgsave-error no ``` --- ### 关键配置关系图 ```mermaid graph LR A[redis.conf]<!--->|dir 参数| B[数据目录] B-->C[磁盘空间] C-->D[写入能力] D-->E[stop-writes-on-bgsave-error] E-->|启用|F[拒绝写入] E-->|禁用|G[允许写入但有风险] ``` #### 预防措施 1. **监控公式**: $$ \text{安全阈值} = \begin{cases} \text{告警} & \text{if } \frac{\text{已用空间}}{\text{总空间}} > 90\% \\ \text{紧急} & \text{if } \frac{\text{已用空间}}{\text{总空间}} > 98\% \end{cases} $$ 2. **目录权限检查脚本**: ```bash #!/bin/bash REDIS_DIR=$(redis-cli config get dir | tail -1) test -w "$REDIS_DIR" || echo "警报:目录不可写!" df -h $REDIS_DIR | awk 'NR==2 {if ($5 > 90) print "磁盘空间不足"}' ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值