Description
Solution
比赛时乱水
开始打题后最先想了这一题:第一题太水,第二题太机智,没办法啊……TAT
然后打了个LIS(用二分n log n进行DP,得出最长不下降子序列)进行暴搜,后来想到可以预处理出所有改变值得最大值和最小值,然后就变成了一个二维的最长不下降子序列,不知道为什么,第一意识就打了个贪心:转移以最大值为第一关键字,a值为第二关键字。
然后竟然100分水过,我还真以为是对的,模模糊糊的讲完题后,被samjia的数据卡掉了。
树套树
作为第一次打树状数组套线段树,也是挺有意义的。
用mx[i]表示在i这个位置变化的最大值,mi[i]表示在i这个位置变化的最小值。(包含初始值)
那么先从DP入手。
f[i]=f[j]+1(j≤i,mx[j]≤a[i],a[j]≤mi[i])
一共要满足三个条件。
条件1,从左到右做很容易满足。
条件2和3有些棘手:我们观察后面的关系式可以发现只与i和j有关,那么我们试着把关系式放到二维平面上表示。那么很显然,在上面的式子都是≤符号是,只需要找所有的 mx[j]≤a[i],a[j]≤mi[i] 。所以每次把点(mx[i],a[i])放入二维平面,然后每次找所有在(1,1)到(a[i],mi[i])这个矩形内的点的最大值。
这个东西用树套树就可以了。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=100007,da=100007;
int i,j,k,l,n,m,ans,r,mid;
int f[maxn],a[maxn],p[maxn],q[maxn],num,mx[maxn],mi[maxn],root[maxn];
bool bz,az;
struct node{
int l,r,sum;
}t[maxn*100];
int lowbit(int x){return x&(-x);}
int find(int x,int l,int r,int y,int z){
int i,o=0;
if(!x)return 0;
if(l==y&&r==z){
return t[x].sum;
}
int mid=(l+r)/2;
if(z<=mid)return find(t[x].l,l,mid,y,z);
else if(y>mid)return find(t[x].r,mid+1,r,y,z);
else{
return max(find(t[x].l,l,mid,y,mid),find(t[x].r,mid+1,r,mid+1,z));
}
}
void change(int &x,int l,int r,int y,int z){
if(!x)x=++num;
t[x].sum=max(t[x].sum,z);
if(l==r)return;
int mid=(l+r)/2;
if(y<=mid)change(t[x].l,l,mid,y,z);
else change(t[x].r,mid+1,r,y,z);
}
int main(){
// freopen("fan.in","r",stdin);
scanf("%d%d",&n,&m);
fo(i,1,n)scanf("%d",&a[i]),mx[i]=mi[i]=a[i];
fo(i,1,m){
scanf("%d%d",&k,&l);mx[k]=max(l,mx[k]);mi[k]=min(l,mi[k]);
}
fo(i,1,n){
int o=0;
for(j=mi[i],p[0]=0;j;j-=lowbit(j))o=max(o,find(root[j],1,da,1,a[i]));
f[i]=o+1;
ans=max(ans,f[i]);
for(j=a[i];j<=da;j+=lowbit(j))change(root[j],1,da,mx[i],f[i]);
}
printf("%d\n",ans);
}