题解 CF650D 【Zip-line】

34 篇文章 0 订阅
4 篇文章 0 订阅

题解- CF650D Zip-line

  • 题目意思

就是给你个序列以及多次操作,每次把 a i ai ai换做 b i bi bi求一遍 l i s lis lis(操作之间互不影响)

  • S o l Sol Sol

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

先对原序列每个点做一遍以他为结束的 l i s lis lis记为 f i fi fi,以他作为起始的 l i s lis lis记为 g i gi gi。易得原序列的 l i s lis lis就为 m a x { f i + g i − 1 } max\{fi+gi-1\} max{fi+gi1}

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

对于 + 1 +1 +1的情况如果 f i ′ + g i ′ + 1 > a n s fi'+gi'+1>ans fi+gi+1>ans那就更新答案

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

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

  • C o d e Code Code
#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]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值