题目描述:
给出两个序列L[i], R[i],求出最长的子序列v[i]满足:
v(1) ≥ 1, v(m) ≤ n,v(i) < v(i + 1). (for i from 1 to m - 1)
L(v(i)) ≥ L(v(i + 1)), R(v(i)) ≤ R(v(i + 1))(for i from 1 to m - 1)
n~1e5;
题解:
转化成求同时两个的最长单调不降子序列.
对于一维的求法:我们认为是(i, li)的单调不降子序列. 如果按照li从小到大的顺序往里面放,那么对于心将要往里放的i,来说, dp[i] = 维护的树状数组的从1到i的最大值. 同样,我们也可以按照i的顺序往里放,然后找<=li的树状数组中的最大值.
对于本题,其实是三维(i,li, ri). 如果我们按照i的顺序往里面放数,那么我们要找到的就是放在树套树结构中同时小于等于li和ri的最大的dp值.这样树套树的方法就有了:如果是两个线段树,那么先进入到1~li的区间的线段树节点来查询.进入节点内部之后: 要以ri为键值,来综合查找出一个最大的dp值.之后把线段树的节点有效值pushUp. 插入的话: 单点插入,一共是nlogn的空间复杂度.
独立于树套平衡树,我们还有一种更巧妙的方法来解这道题目.
对于(i,li,ri),想cdq分治.分治有什么好处呢? 只需要思考左端对右端的贡献就好,这样就会减少一维i!!!!!! 然后我们按照li的大小顺序把一个一个数放进去,用树状数组维护小于等于ri的最大的dp值,来+1作为dp[i]的值更新到树状数组中. 注意其中能够更新的只有mid+1到r的i, l到mid的i是不能更新的,只能放进去. 并且mid+1到r的是不能放到树状数组里面的. 和归并操作一样.这样完了之后再递归处理右边.
重点:
(1)想明白和以为的单调不降子序列的关系.以及熟练掌握用树状数组求单调不降子序列的思想,放进去的顺序的思维很好.
(2)用树状数组的思想来求3维不降子序列,考虑到树套树.
(3)三维减少一维的方法是分治.想明白cdq分治的影响部分.
代码:
cdq分治.需要注意的地方很多
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n;
struct node {
int l, r, idx;
friend bool operator < (node x, node y) {
return x.l < y.l || (x.l == y.l && x.r < y.r) || (x.l == y.l && x.r == y.r && x.idx < y.idx);
}
}p[50010];
int hs[50010];
struct ans_node {
int sum, idx;
ans_node(int _sum = 0, int _pre = 0) {
sum = _sum;
idx = _pre;
}
friend bool operator < (ans_node x, ans_node y) {
return x.sum < y.sum || x.sum == y.sum && x.idx > y.idx;
}
}C[50010], f[50010];
int ans, ans_pos, prepre[50010];
ans_node getMax(int x) {
ans_node ret = ans_node(0, 0);
while (x > 0) {
ret = max(ret, C[x]);
x -= x & -x;
}
return ret;
}
bool cmp(node x, node y) {
return x.idx < y.idx;
}
void solve(int l, int r) {
//printf("@#$%d %din\n", l, r);
if (l == r) {
if (f[l].sum == 0) {
f[l].sum = 1;
f[l].idx= l;
}
//printf("@#$%d %dout\n", l, r);
return;
}
int mid = (l + r) >> 1;
solve(mid + 1, r);
int i = l, j = mid + 1;
sort(p + l, p + mid + 1);
sort(p + mid + 1, p + r + 1);
int hs_len = 0;
for (int i = mid + 1; i <= r; ++i) {
hs[hs_len++] = p[i].r;
}
sort(hs, hs + hs_len);
hs_len = unique(hs, hs + hs_len) - hs;
for (int i = 0; i <= hs_len; ++i) {
C[i] = 0;
}
for (int i = l; i <= mid; ++i) {
int pos;
while (j <= r && p[j].l <= p[i].l) {
pos = lower_bound(hs, hs + hs_len, p[j].r) - hs + 1;
while (pos <= hs_len) {
C[pos] = max(C[pos], f[p[j].idx]);
pos += pos & -pos;
}
j++;
}
pos = upper_bound(hs, hs + hs_len, p[i].r) - hs;
ans_node max_nxt = getMax(pos), max_now = ans_node(f[p[i].idx].sum, prepre[p[i].idx]);
max_nxt.sum++;
if (max_now < max_nxt) {//注意,不能直接更新,因为以前的数可能会已经产生了影响.
f[p[i].idx] = max_nxt;
prepre[p[i].idx] = f[p[i].idx].idx;
//printf("------%d %d\n", p[i].idx, f[p[i].idx].idx);
f[p[i].idx].idx = p[i].idx;
}
}
sort(p + l, p + mid + 1, cmp);
solve(l, mid);
//printf("@#$%d %dout\n", l, r);
}
int main() {
//freopen("1009.in", "r", stdin);
//freopen("out.txt", "w", stdout);
while (~scanf("%d", &n)) {
memset(f, 0, sizeof f);
memset(prepre, 0, sizeof prepre);
for (int i = 1; i <= n; ++i) {
scanf("%d", &p[i].l);
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &p[i].r);
p[i].r = -p[i].r;
p[i].idx = i;
}
solve(1, n);
int ans = 0, ans_pos = 1000010;
for (int i = 1; i <= n; ++i) {
if (f[i].sum > ans || f[i].sum == ans && f[i].idx < ans_pos) {
ans = f[i].sum;
ans_pos = f[i].idx;
}
}
printf("%d\n", ans);
printf("%d", ans_pos);
for (int k = prepre[ans_pos]; k > 0; k = prepre[k]) {
printf(" %d", k);
}
printf("\n");
}
return 0;
}
///树套平衡树. 还没学习= = 这是别人的代码.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 50010;
struct Node{
Node *ch[2];
int r;
int v; //第二维
int len; //长度
int idx; //下标
int mlen; //结点和结点左孩子右孩子里最大长度
int midx; //结点和结点左孩子右孩子里最大长度对应的下标
Node() {}
Node(int v, int len, int idx):v(v),len(len),idx(idx),mlen(len),midx(idx) {ch[0] = ch[1] = NULL; r = rand();}
int cmp(int x) const {
if (x == v) return -1;
return x < v ? 0 : 1;
}
void maintain(){
mlen = len;
midx = idx;
if (ch[0] != NULL && (ch[0]->mlen > mlen || (ch[0]->mlen == mlen && ch[0]->midx < midx))){
mlen = ch[0]->mlen;
midx = ch[0]->midx;
}
if (ch[1] != NULL && (ch[1]->mlen > mlen || (ch[1]->mlen == mlen && ch[1]->midx < midx))){
mlen = ch[1]->mlen;
midx = ch[1]->midx;
}
}
};
bool findMax(Node* a, Node* b){
if (a->mlen < b->mlen || (a->mlen == b->mlen && a->midx > b->midx)){
*a = *b;
return true;
}
return false;
}
namespace Treap{
int cntnode;
Node node[maxn*10];
void init(){
cntnode = 0;
}
Node* newNode(int v, int len, int idx){
node[++cntnode] = Node(v, len, idx);
return &node[cntnode];
}
void rotate(Node* &o, int d){
Node* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o;
o->maintain(); k->maintain(); o = k;
}
void insert(Node* &o, int v, int len, int idx){
if (o == NULL) o = newNode(v, len, idx);
else {
int d = o->cmp(v);
if (d != -1){
insert(o->ch[d], v, len, idx);
if (o->r < o->ch[d]->r) rotate(o, d^1);
}
else
{
if (len >= o->len){
o->len = len;
o->idx = idx;
}
}
}
o->maintain();
}
Node search(Node *o, int v){
if (o == NULL){
return Node(-1, 0, -1);
}
else{
Node re, tmp;
if (o->v == v) {
re = Node(o->v, o->len, o->idx);
if (o->ch[1]){
findMax(&re, o->ch[1]);
}
}
else if (o->v > v){
re = Node(o->v, o->len, o->idx);
if (o->ch[1]){
findMax(&re, o->ch[1]);
}
if (o->ch[0]){
tmp = search(o->ch[0], v);
findMax(&re, &tmp);
}
}
else{
re = search(o->ch[1], v);
}
return re;
}
}
}
namespace BIT{
Node* fwt[maxn];
int N;
void init(int n){
N = n;
memset(fwt, 0, sizeof fwt);
}
void add(int v1, int v2, int len, int idx){
while(v1 < N){
Treap::insert(fwt[v1], v2, len, idx);
v1 += (-v1)&v1;
}
}
Node query(int v1, int v2){
Node re, tmp;
re = Node(-1, 0, -1);
while(v1 > 0){
tmp = Treap::search(fwt[v1], v2);
findMax(&re, &tmp);
v1 -= (-v1)&v1;
}
return re;
}
}
struct Pe{
int L,R;
int i;
bool operator < (const Pe& rhs)const{
return L < rhs.L;
}
};
bool cmp(Pe a, Pe b){
return a.i < b.i;
}
int solo[maxn];
Pe pe[maxn];int pre[maxn];
void print(Node& a){
int id = a.midx;
printf("%d\n", a.mlen);
while(1){
printf("%d", id+1);
if (pre[id] == -1){
break;
}
printf(" ");
id = pre[id];
}
printf("\n");
}
int main(){
int n;
while(scanf("%d", &n) != EOF){
for(int i=0;i<n;i++){
pe[i].i = i;
scanf("%d", &pe[i].L);
}
for(int i=0;i<n;i++){
scanf("%d", &pe[i].R);
}
sort(pe, pe+n);
int m = 0;
solo[0] = ++m;
for(int i=1;i<n;i++){
if (pe[i].L != pe[i-1].L){
solo[i] = ++m;
}
else{
solo[i] = solo[i-1];
}
}
for(int i=0;i<n;i++){
pe[i].L = solo[i];
}
sort(pe, pe+n, cmp);
BIT::init(m+1);
Treap::init();
Node ans = Node(-1, 0, -1), tmp;
for(int i=n-1;i>=0;i--){
tmp = BIT::query(pe[i].L, pe[i].R);
pre[i] = tmp.midx;
tmp.mlen = tmp.mlen + 1;
tmp.midx = i;
BIT::add(pe[i].L, pe[i].R, tmp.mlen, tmp.midx);
findMax(&ans, &tmp);
}
print(ans);
}
return 0;
}