BZOJ 4553 [Tjoi2016&Heoi2016]序列 线段树套treap

Description

 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你
,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可
。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:
1 2 3
2 2 3
1 3 3
1 1 31 2 4
选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列
为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求

Input

 输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的

状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。所有数字均为正整数
,且小于等于100,000

Output

 一个整数

Sample Input

3 4
1 2 3
1 2
2 3
2 1
3 4

Sample Output

3

HINT











注意题目里要求是所有情况下都满足,
用MAX(i)表示i位置能取的最大值,MIN(i)表示i位置能取的最小值,
那么对于一个i,假设有一个j,i和j能在序列里的条件是什么呢?
为了方便假设j<i,
那么MAX(j)<=a[i]并且a[j]<=MIN(i),注意只会改动一个数,讨论谁会改动即可。
由此我们就可以得到一个O(N^2)的dp了。。

然后考虑去优化它。
由于同时要满足两个条件
(事实上还有个j<i,所以实际上是三维偏序,
而j,i的顺序只要1~n枚举就直接确定了所以剩余一个二维偏序)
那么有两种方法,一种是cdq,考虑左区间对有区间的影响,树状数组搞搞即可
(我会说我不会吗= =);
还有就是暴力点的树套树了。。
由于求的是1~x中的最值,所以外层树状数组完全就可以搞了= =
内层则必须线段树或者平衡树了。。
而我一开始写了外层线段树,后面又懒得改了= =于是自信常数提交了一发。。
。。
17s,目前排名倒数第4……
幸好了写个splay都AC不了了。。。。
时间什么的不重要
我错了一定要去学学cdq

orz各位神犇都是1A的……我up写错结果WA了几次。。QAQ







#include<bits/stdc++.h>
using namespace std;
const int 
	N=100005,
	Nlog=1800005;
int n,m,maxnum,ans,tot;
int a[N],MAX[N],MIN[N],root[N<<2];
struct Treap{
	int l[Nlog],r[Nlog];
	int NUM[Nlog],MAX[Nlog],rnd[Nlog],val[Nlog];
	void up(int u){
		MAX[u]=max(NUM[u],max(MAX[l[u]],MAX[r[u]]));
	}
	void lturn(int &u){
		int t=r[u];r[u]=l[t],l[t]=u;
		up(u),u=t,up(u);
	}
	void rturn(int &u){
		int t=l[u];l[u]=r[t],r[t]=u;
		up(u),u=t,up(u);
	}
	void insert(int &u,int x,int &tot,int y){
		if (!u){
			u=++tot;
			NUM[u]=MAX[u]=y;
			l[u]=r[u]=0,val[u]=x,rnd[u]=rand();
			return;
		}
		MAX[u]=max(MAX[u],y);
		if (x==val[u]){NUM[u]=max(NUM[u],y);return;}
		if (x<val[u]){
			insert(l[u],x,tot,y);
			if (rnd[l[u]]<rnd[u]) rturn(u);
		} else{
			insert(r[u],x,tot,y);
			if (rnd[r[u]]<rnd[u]) lturn(u);
		}
	}
	void get_MAX(int u,int x){
		if (!u) return;
		if (val[u]<=x){
			ans=max(ans,max(MAX[l[u]],NUM[u]));
			get_MAX(r[u],x);
		} else get_MAX(l[u],x);
	}
}tr;
void update(int id,int l,int r,int g,int g1,int num){
	tr.insert(root[id],g1,tot,num);
	if (l==r) return;
	int mid=(l+r)>>1;
	if (g<=mid) update(id<<1,l,mid,g,g1,num);
	 else update(id<<1|1,mid+1,r,g,g1,num);
}
int query(int id,int l,int r,int gl,int gr,int num){
	if (l>=gl && r<=gr){
		ans=0,tr.get_MAX(root[id],num);
		return ans;
	}
	int mid=(l+r)>>1,t=0;
	if (gl<=mid) t=max(t,query(id<<1,l,mid,gl,gr,num));
	if (gr>mid) t=max(t,query(id<<1|1,mid+1,r,gl,gr,num));
	return t;
}
int main(){
	srand(123215);
	scanf("%d%d",&n,&m),maxnum=0;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),MAX[i]=MIN[i]=a[i],
		maxnum=max(maxnum,a[i]);
	int x,y;
	for (int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		maxnum=max(maxnum,y);
		MAX[x]=max(MAX[x],y),MIN[x]=min(MIN[x],y);
	}
	tot=0;int ANS=1;
	update(1,1,maxnum,a[1],MAX[1],1);
	for (int i=2;i<=n;i++){
		int t=query(1,1,maxnum,1,MIN[i],a[i])+1;
		update(1,1,maxnum,a[i],MAX[i],t);
		ANS=max(ANS,t);
	}
	printf("%d\n",ANS);
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值