JZOJ 5920. 【NOIP2018模拟10.22】风筝

题目

有一个长度为n的序列和m个询问。
每个询问:将a[u]替换成v后,最长严格上升子序列的长度。
每个询问互相独立。
n,m<=5e5

题解

分类讨论。
a[u]被修改为v时,严格上升子序列有两种,一种是不含位置u的,一种是含位置u的。
不含u的,需要预处理出每个询问的v顺着做的排名f1[i],倒着做的排名g1[i]。
设f[i],g[i]表示顺着/倒着做,i在lis中的排名。
如果删去a[u]后,仍然有元素j,满足f[i]=f[j],g[i]=g[j],且f[i]+g[i]-1=lis,那么lis不会减小。
如果含v,那么那么f1[i]+g1[i]-1则是这种情况的最佳答案。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define N 500010
#define Q 8388608
#define Inf 2147483647
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--) 
using namespace std;
struct noq{
	int u,v,id;
}qu[N];
int ti[N];
int a[N],b[N],c[N],f[N],g[N];
int f1[N],g1[N];
int L[N],R[N],tar[N],ans[N],ha[Q],Ha[Q];
int i,j,k,l1,r1,mid;
int n,m,x,wz,lis,cs,Lis,z[N];
int u,v,w,w1,Mx,Mn,W;
int opl,opr,opx,opz;
int read(){
	int fh=0,rs=0;char ch=0;
	while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
	if(ch=='-')fh=1,ch=getchar();
	while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
	return fh?-rs:rs;
}
void write(int x){
	if(x>9)write(x/10);
	P(x%10+'0');
}
int hash(int x){
	int y=x&(Q-1);
	while(ha[y]&&ha[y]!=x)y=(y+1)&(Q-1);
	ha[y]=x;
	return y;
}
int ef(int L,int R,int num){
	int x=R,Mid;
	while(L<=R){
		Mid=(L+R)>>1;
		if(c[Mid]>num)x=Mid,R=Mid-1;else L=Mid+1;
	}
	return x;
}
int ef1(int L,int R,int num){
	int x=R,Mid;
	while(L<=R){
		Mid=(L+R)>>1;
		if(c[Mid]<num)x=Mid,R=Mid-1;else L=Mid+1;
	}
	return x;
}
int Max(int x,int y){return x>y?x:y;}
int Min(int x,int y){return x<y?x:y;}
bool cmp(noq a,noq b){return a.u<b.u||(a.u==b.u&&a.v<b.v);}
bool cmp1(noq a,noq b){return a.u>b.u||(a.u==b.u&&a.v>b.v);}
bool cmp2(noq a,noq b){return a.id<b.id;}
int main(){
	n=read();m=read();
	fo(i,1,n)a[i]=read();
	fo(i,1,m)qu[i].u=read(),qu[i].v=read(),qu[i].id=i;
	sort(qu+1,qu+m+1,cmp);
	lis=0;W=1;
	fo(i,1,n){
		c[lis+1]=Inf;
		while(W<=m&&qu[W].u==i){
			wz=ef(1,lis+1,qu[W].v);
			if(c[wz-1]==qu[W].v)wz--;
			f1[qu[W].id]=wz;
			W++;
		}
		wz=ef(1,lis+1,a[i]);
		if(c[wz-1]<a[i]){
			f[i]=f[b[wz-1]]+1;
			c[wz]=a[i];
			b[wz]=i;
			if(wz==lis+1)lis++;
		}else f[i]=f[b[wz-1]];
	}
	fo(i,1,n)b[i]=0;
	Lis=lis;lis=0;
	c[0]=Inf;
	sort(qu+1,qu+m+1,cmp1);
	W=1;
	fd(i,n,1){
		c[lis+1]=Inf;
		while(W<=m&&qu[W].u==i){
			wz=ef1(1,lis+1,qu[W].v);
			if(c[wz-1]==qu[W].v)wz--;
			g1[qu[W].id]=wz;
			W++;
		}
		wz=ef1(1,lis+1,a[i]);
		if(c[wz-1]>a[i]){
			g[i]=g[b[wz-1]]+1;
			c[wz]=a[i];
			b[wz]=i;
			if(wz==lis+1)lis++;
		}else g[i]=g[b[wz-1]];
		if(f[i]+g[i]-1==Lis){
			z[i]=(1ll*123456789*f[i]+g[i])%1000000007;
		    w=hash(z[i]);
		    Ha[w]++;
		}
	}
	sort(qu+1,qu+m+1,cmp2);
	fo(i,1,m){
		w=hash(z[qu[i].u]);
		if(Ha[w]>1||w==0)ans[i]=Lis;else ans[i]=Lis-1;
		ans[i]=Max(ans[i],f1[i]+g1[i]-1);
		write(ans[i]);P('\n');
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值