「TJOI / HEOI2016」序列
题意:
玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可。
解法:
我们可以预处理处每个点最大可能变成的值(r_i),最小可能变成的值(l_i),显然,如果一个子序列符合要求,那么必然有(r_{i-1}leq a_i和a_{i-1}leq l_i)
所以我们很容易写出转移方程(f_i=max_{j=1}^{i-1} (f[j]+1)) , 如果 (r_jleq a_i,a_jleq l_i)
方法1
很显然,我们可以把限制条件看成二维点对,用树套树维护最大值即可。
我写了线段树套线段树TLE了,后来改成树状数组就过了。代码在最后会附上。
方法2
加上j<i这一条件,我们易想到三维偏序问题,考虑cdq来解决,这个常数会比树套树小。
首先,由于限制的条件不同于一般的三位偏序,我们考虑lmid对mid+1r的答案影响时,我们不像归并那样一个个插入,我们每次分治lr时,都将lmid按r要素排序,mid+1~r按a要素排序,对于某个(r_i>a_j),这以后的i对于j的答案就没有影响了,用树状数组找出答案就行了。
此外,考虑到每个(f_i)的最优值需要所有小于i的pos最优值都已经求出来,所以我们需要先处理左半边,在处理自己,最后处理右边。
线段树套线段树
#include <bits/stdc++.h>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int maxn = 1e5;
int f[maxn + 11],a[maxn + 11],l[maxn + 11],r[maxn + 11];
int tree[200 * maxn + 11],ls[200 * maxn + 11],rs[200 * maxn + 11];
int root[4 * maxn + 11];
int tot = 0;
int m;
void push_up(int rt) { tree[rt] = max(tree[ls[rt]] , tree[rs[rt]]); }
void update(int &rt,int l,int r,int pos,int val) {
if (l > pos || r < pos) return;
if (!rt) rt = ++tot;
if (l == r) { tree[rt] = max(tree[rt] , val); return; }
int mid = (l + r) >> 1;
update(ls[rt] , l , mid , pos , val);
update(rs[rt] , mid + 1 , r , pos , val);
push_up(rt);
}
void update(int rt,int l,int r,int x,int y,int val) {
if (l > x || r < x) return;
update(root[rt] , 1 , m , y , val);
if (l == r) return;
int mid = (l + r) >> 1;
update(lson , l , mid , x , y , val);
update(rson , mid + 1 , r , x , y, val);
}
int query(int rt,int l,int r,int al,int ar) {
if (l > ar || r < al || !rt) return 0;
if (l >= al && r <= ar) return tree[rt];
int mid = (l + r) >> 1;
return max(query(ls[rt] , l , mid , al , ar) , query(rs[rt] , mid + 1 , r , al , ar));
}
int query(int rt,int l,int r,int xl,int xr,int yl,int yr) {
if (l > xr || r < xl) return 0;
if (l >= xl && r <= xr) return query(root[rt] , 1 , m , yl , yr);
int mid = (l + r) >> 1;
return max(query(lson , l , mid , xl , xr , yl , yr) , query(rson , mid + 1 , r , xl , xr , yl , yr));
}
int main() {
int n;
scanf("%d %d",&n,&m);
int mx = 0;
for (int i = 1; i <= n; i++) {
scanf("%d" , &a[i]);
l[i] = r[i] = a[i];
mx = max(mx , a[i]);
}
for (int i = 1; i <= m; i++) {
int x,y;
scanf("%d %d",&x,&y);
l[x] = min(l[x] , y);
r[x] = max(r[x] , y);
mx = max(mx , y);
}
m = mx;
for (int i = 1; i <= n; i++) {
f[i] = query(1 , 1 , m , 1 , a[i] , 1 , l[i]) + 1;
update(1 , 1 , m , r[i] , a[i] , f[i]);
}
printf("%d
" , tree[1]);
}
树状数组套线段树
#include <bits/stdc++.h>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int maxn = 1e5;
int tot = 0;
int f[maxn + 11],a[maxn + 11],l[maxn + 11],r[maxn + 11],root[maxn + 11];
int tree[200 * maxn + 11],rs[200 * maxn + 11],ls[200 * maxn + 11];
int m;
void update(int &rt,int l,int r,int pos,int val) {
if (l > pos || r < pos) return;
if (!rt) rt = ++tot;
tree[rt] = max(tree[rt] , val);
if (l == r) return;
int mid = (l + r) >> 1;
update(ls[rt] , l , mid , pos , val);
update(rs[rt] , mid + 1 , r , pos , val);
}
int query(int rt,int l,int r,int al,int ar) {
if (l > ar || r < al || !rt) return 0;
if (l >= al && r <= ar) return tree[rt];
int mid = (l + r) >> 1;
return max(query(ls[rt] , l , mid , al , ar) , query(rs[rt] , mid + 1 , r , al , ar));
}
int main() {
int n;
scanf("%d %d",&n,&m);
int mx = 0;
for (int i = 1; i <= n; i++) {
scanf("%d" , &a[i]);
l[i] = r[i] = a[i];
mx = max(mx , a[i]);
}
for (int i = 1; i <= m; i++) {
int x,y;
scanf("%d %d",&x,&y);
l[x] = min(l[x] , y);
r[x] = max(r[x] , y);
mx = max(mx , y);
}
m = mx;
int ans = 0;
for (int i = 1; i <= n; i++) {
mx = 0;
for (int j = a[i]; j; j -= (j & (-j))) mx = max(mx , query(root[j] , 1 , m , 1 , l[i]));
f[i] = mx + 1;
for (int j = r[i]; j <= m; j += (j & (-j))) update(root[j] , 1 , m , a[i] , f[i]);
ans = max(ans , f[i]);
}
printf("%d
" , ans);
}
cdq分治
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5;
int m;
int bit[maxn + 11],ans[maxn + 11];
struct node {
int a,l,r,i;
}p[maxn + 11];
bool cmpr(node x,node y) { return x.r < y.r; }
bool cmpa(node x,node y) { return x.a < y.a; }
bool cmpi(node x,node y) { return x.i < y.i; }
int lowbit(int x) { return x & (-x); }
void update(int x,int val) { for (; x <= m; x += lowbit(x)) bit[x] = max(bit[x] , val); }
void clear(int x) { for (; x <= m; x += lowbit(x)) bit[x] = 0; }
int query(int x) { int ans = 0; for (; x ; x -= lowbit(x)) ans = max(ans , bit[x]); return ans; }
void cdq(int l,int r) {
if (l >= r) return;
int mid = (l + r) >> 1;
cdq(l , mid);
sort(p + l , p + mid + 1 , cmpr);
sort(p + mid + 1 , p + r + 1 , cmpa);
int i,j;
for (i = l , j = mid + 1; i <= mid && j <= r;) {
if (p[i].r <= p[j].a) { update(p[i].a , ans[p[i].i]); i++; }
else { ans[p[j].i] = max(ans[p[j].i] , 1 + query(p[j].l)); j++; }
}
for (; j <= r; j++) ans[p[j].i] = max(ans[p[j].i] , 1 + query(p[j].l));
for (i--; i >= l; i--) clear(p[i].a);
sort(p + mid + 1 , p + r + 1 , cmpi);
cdq(mid + 1 , r);
}
int main(){
int n;
scanf("%d %d",&n,&m);
int mx = 0;
for (int i = 1; i <= n; i++) {
scanf("%d",&p[i].a);
p[i].l = p[i].r = p[i].a;
p[i].i = i;
mx = max(mx , p[i].a);
ans[i] = 1;
}
for (int i = 1; i <= m; i++) {
int x,y;
scanf("%d %d",&x,&y);
p[x].l = min(p[x].l , y);
p[x].r = max(p[x].r , y);
mx = max(mx , y);
}
m = mx;
cdq(1 , n);
mx = 0;
for (int i = 1; i <= n; i++) mx = max(mx , ans[i]);
printf("%d
" , mx);
}