[CF650D]Zip-line

Zip-line

题解

虽然老师是叫我们用树状数组做的,但根本不需要什么数据结构

首先 O ( n l o g   n ) O(nlog\,n) O(nlogn)维护最长上升子序列的技巧是很简单的,相信大家应该都会。不会的自己去看一下就行了
那么该改变一个节点后,答案又会怎样变化呢?
首先答案有两种可能,一个是经过该节点,一个是不经过该节点。
经过该节点的答案必然有三部分组成,左边的最长上升子序列,右边的最长下降子序列,与它自身。
前面两者是很好维护的,我们可以记录下某个节点前最长上升子序列为 i i i时最后一个节点的最小值,二分找到小于它的最大的那个点,右边同理。
由于这样,我们需要将询问离线下来,按 a a a值排序,在求最长上升子序列的同时得到它左右的值。
如果强制在线的话就只能用可持久化线段树了。
左边的上升序列与右边的下降序列都跑后就可以得到这部分的答案了。

而不经过该节点的序列,其长度是不会小于原序列的最长上升子序列的长度减一的。
而这个长度减一,当且仅当它是原序列的桥时才会取等。
所以我们可以记录下来前缀最长上升子序列为 x x x且可以转移到最后答案的位置有那些。
如果某个点对于其 x x x是唯一的,那它就是原序列的桥。
这部分可以像上面的询问一样处理。
两者求个最大值即可得到答案。

时间复杂度 O ( n log ⁡   n ) O\left(n\log\,n\right) O(nlogn)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 400005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=9901;
const int inv2=499122177;
const int jzm=2333;
const int lim=1000000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,h[MAXN],ans[MAXN],sta[MAXN],stak,cl[MAXN],cr[MAXN];
vector<int>vec[MAXN];bool vis[MAXN];
struct ming{int a,b,id;}s[MAXN];
bool cmp(ming x,ming y){return x.a<y.a;}
signed main(){
	read(n);read(m);
	for(int i=1;i<=n;i++)read(h[i]);
	for(int i=1;i<=m;i++)read(s[i].a),read(s[i].b),s[i].id=i;
	sort(s+1,s+m+1,cmp);int j=1;
	for(int i=1;i<=n;i++){
		while(j<=m&&s[j].a==i){int l=0,r=stak;while(l<r){int mid=l+r+1>>1;if(sta[mid]<s[j].b)l=mid;else r=mid-1;}ans[s[j].id]+=l;j++;}
		int l=0,r=stak;while(l<r){int mid=l+r+1>>1;if(sta[mid]<h[i])l=mid;else r=mid-1;}
		cl[i]=l;if(l==stak)stak++,sta[stak]=h[i];sta[l+1]=min(sta[l+1],h[i]);
	}
	int maxlen=stak;stak=0;j=m;sta[0]=lim+1;
	for(int i=n;i>0;i--){
		while(j>0&&s[j].a==i){int l=0,r=stak;while(l<r){int mid=l+r+1>>1;if(sta[mid]>s[j].b)l=mid;else r=mid-1;}ans[s[j].id]+=l;j--;}
		int l=0,r=stak;while(l<r){int mid=l+r+1>>1;if(sta[mid]>h[i])l=mid;else r=mid-1;}
		cr[i]=l;while(l==stak)stak++,sta[stak]=h[i];sta[l+1]=max(sta[l+1],h[i]);
	}
	for(int i=1;i<=n;i++)if(cl[i]+cr[i]+1==maxlen)vec[cl[i]+1].push_back(i);
	for(int i=1;i<=maxlen;i++)
		if(vec[i].size()==1)vis[vec[i][0]]=1;
	for(int i=1;i<=m;i++)
		if(vis[s[i].a])ans[s[i].id]=max(ans[s[i].id]+1,maxlen-1);
		else ans[s[i].id]=max(ans[s[i].id]+1,maxlen);
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值