题解 CF650D 【Zip-line】

本文详细解析了CF650D Zip-line问题的解决方案,通过预处理得到最长递增子序列(LIS)的前缀和后缀信息,结合离线查询和树状数组技巧,高效处理序列修改对LIS的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解- CF650D Zip-line

  • 题目意思

就是给你个序列以及多次操作,每次把aiaiai换做bibibi求一遍lislislis(操作之间互不影响)

  • SolSolSol

显然每次修改暴力做lislislis是不可行的复杂度至少为O(n2log⁡n)O(n^2 \log n )O(n2logn)。于是我们要思考每次修改会对答案形成怎样的影响。

先对原序列每个点做一遍以他为结束的lislislis记为fififi,以他作为起始的lislislis记为gigigi。易得原序列的lislislis就为max{fi+gi−1}max\{fi+gi-1\}max{fi+gi1}

每次对于修改一个值分几种情况来考虑 (思路借鉴 霜雪谦年 的题解),我们记修改过后的fifififi′fi'figi′gi'gi同理。

对于+1+1+1的情况如果fi′+gi′+1>ansfi'+gi'+1>ansfi+gi+1>ans那就更新答案

对于−1-11的情况相对复杂一点。我们要先判断此次修改的点是否为原序列lislislis的必经之点,判断的方法很简单如果fi+gi−1=ansfi+gi-1=ansfi+gi1=ans,并且这样的情况有且只有一种。

剩余的就是不变的情况了。于是我们这道题目就做完了。求lislislis可以使用树状数组来实现fififi正着gigigi倒着即可并且要离散化一下就可以了。用离线来实现。

  • CodeCodeCode
#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;

const int N=800005;

int n,m,ans,f[N],g[N],tr[N];
int a[N],b[N],res[N],cs[N],cnt;

struct number {
	int id,x,val;
	int f,g;
	inline bool friend operator < (const number &a,const number &b) {
		if(a.x==b.x) return a.val<b.val;
		return a.x<b.x;
	}
};
number q[N];

inline int read() {
	int sum=0; char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) 
		sum=sum*10+(ch^48),ch=getchar();
	return sum;
}

inline void jia(int x,int v) {
	while(x<=cnt) {
		tr[x]=max(tr[x],v);
		x+=lowbit(x);
	}
}

inline int query(int x) {
	int ret=0;
	while(x) {
		ret=max(ret,tr[x]);
		x-=lowbit(x);
	}
	return ret;
}

int main() {
	n=read(),m=read();
	cnt=n;
	for ( int i=1;i<=n;i++ ) {
		a[i]=read();
		b[i]=a[i];
	}
	for ( int i=1;i<=m;i++ ) {
		q[i].id=i;
		q[i].x=read();
		q[i].val=read();
		b[++cnt]=q[i].val;
	}
	sort(b+1,b+cnt+1);
	cnt=unique(b+1,b+cnt+1)-b-1;
	for ( int i=1;i<=n;i++ ) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
	for ( int i=1;i<=m;i++ ) q[i].val=lower_bound(b+1,b+cnt+1,q[i].val)-b;
	for ( int i=1;i<=n;i++ ) {
		f[i]=query(a[i]-1)+1;
		jia(a[i],f[i]);
	}
	memset(tr,0,sizeof(tr));
	for ( int i=n;i>=1;i-- ) {
		g[i]=query(cnt-a[i])+1;
		jia(cnt-a[i]+1,g[i]);
	}
	for ( int i=1;i<=n;i++ ) ans=max(ans,f[i]+g[i]-1);
	for ( int i=1;i<=n;i++ ) if(f[i]+g[i]-1==ans) cs[f[i]]++;
	sort(q+1,q+m+1);
	memset(tr,0,sizeof(tr));
	int now=1;
	for ( int i=1;i<=m;i++ ) {
		while(now<q[i].x) {
			jia(a[now],f[now]);
			now++;
		}
		q[i].f=query(q[i].val-1);
	}
	memset(tr,0,sizeof(tr));
	now=n;
	for ( int i=m;i>=1;i-- ) {
		while(now>q[i].x) {
			jia(cnt-a[now]+1,g[now]);
			now--;
		}
		q[i].g=query(cnt-q[i].val);
		if(q[i].f+q[i].g+1>ans) res[q[i].id]=q[i].f+q[i].g+1;
	}
	for ( int i=1;i<=m;i++ ) if(!res[q[i].id]) {
		if(f[q[i].x]+g[q[i].x]==ans+1&&cs[f[q[i].x]]==1&&q[i].f+q[i].g+1<ans) res[q[i].id]=ans-1;
		else res[q[i].id]=ans;
	}
	for ( int i=1;i<=m;i++ ) printf("%d\n",res[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值