【洛谷4093】[HEOI2016/TJOI2016] 序列(CDQ分治)

点此看题面

  • 给定一个长度为(n)的序列(a),给定(m)个条件形如(a_x)可以变成(y)
  • 一种情况下只有最多一个值可能发生变化,求一个最长子序列,满足在所有情况下它都单调不降。
  • (nle10^5)

读清题面!

一开始看错题面,没有看到一次最多一个值可能发生变化的限制,想自闭了。

随手点开洛谷讨论一看一堆人说数据太水,题目读错都能水到好多分。

本以为他们是小丑,居然会有这么多人都把题目读错了,然后重新看了遍题面,发现小丑竟是我自己。。。

三维偏序

只有一个值可能发生变化就非常简单了,记录一下每个位置可能达到的最小值(Mn_i)和最大值(Mx_i)

两个位置(i,j)(i<j))能同时被选入子序列,当且仅当(a_ile Mn_j)(j)发生了变化)且(Mx_ile a_j)(i)发生了变化)。

粗略一看,这似乎是四维偏序,不,甚至是跨维度偏序,好像非常诡异。

但实际上,关于下标这一维的顺序是比较明确的,而一旦确定了(i<j),我们就可以把(i)(a,Mx)分别看作二三维,(j)(Mn,a)分别看作二三维,就变成了一个三维偏序问题。

所以直接上(CDQ)分治即可。

代码:(O(nlog^2n))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,m,f[N+5];struct Data
{
	int p,x,Mn,Mx;I Data(CI i=0,CI a=0,CI b=0,CI c=0):p(i),x(a),Mn(b),Mx(c){}
	I bool operator < (Con Data& o) Con {return p<o.p;}
}s[N+5];
I bool cx(Con Data& x,Con Data& y) {return x.x<y.x;}
I bool cMn(Con Data& x,Con Data& y) {return x.Mn<y.Mn;}
I bool cMx(Con Data& x,Con Data& y) {return x.Mx<y.Mx;}
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
struct TreeArray
{
	int a[N+5];I void C(RI x) {W(x<=n&&a[x]) a[x]=0,x+=x&-x;}//从x开始清空
	I void U(RI x,CI v) {W(x<=N&&a[x]<v) a[x]=v,x+=x&-x;}//单点修改
	I int Q(RI x,RI t=0) {W(x) t=max(t,a[x]),x-=x&-x;return t;}//询问前缀最大值
}T;
I void CDQ(CI l,CI r)//CDQ分治,以下标为第一维
{
	if(l==r) return;RI mid=l+r>>1;CDQ(l,mid);//先做左区间
	RI i=l,j=mid+1;for(sort(s+l,s+mid+1,cx),sort(s+mid+1,s+r+1,cMn);j<=r;//按照各自的第二维排序
		f[s[j].p]=max(f[s[j].p],T.Q(s[j].x)),++j) W(i<=mid&&s[i].x<=s[j].Mn) T.U(s[i].Mx,f[s[i].p]+1),++i;//双指针,根据各自的第三维在树状数组上操作
	W(--i>=l) T.C(s[i].Mx);sort(s+mid+1,s+r+1),CDQ(mid+1,r);//清空,重新将右区间按第一维排序,再做右区间
}
int main()
{
	RI i;for(read(n,m),i=1;i<=n;++i) read(s[i].x),s[i].Mn=s[i].Mx=s[i].x,f[s[i].p=i]=1;
	RI x,y;for(i=1;i<=m;++i) read(x,y),s[x].Mn=min(s[x].Mn,y),s[x].Mx=max(s[x].Mx,y);//记录每个位置能达到的最小值和最大值
	RI t=0;for(CDQ(1,n),i=1;i<=n;++i) t=max(t,f[i]);return printf("%d
",t),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4093.html