[TJOI / HEOI2016]序列——CDQ分治

题面

  LOJ#2056

解析

   居然是$CDQ$分治?

  先不考虑时间复杂度

  显然是一个$dp$,设$dp[i]$是已下标$i$为结尾的最长不下降子序列,则$dp[i] = max\left \{ dp[j] \right \} + 1$

  其中下标$j$需要满足以下条件,设在下标$j$处的值最大为$mx[j]$,最小为$mn[j]$,原值为$val[j]$:

  $1 \leqslant j < i$

  $val[j] \leqslant mn[i]$

  $mx[j] \leqslant val[i]$

  于是惊奇的发现这是一个三维偏序,$CDQ$分治直接上

  可能是写法的问题,这道题的$CDQ$分治的过程与我往常做的$CDQ$分治的过程有些不同

  往常是把左区间与右区间的信息分别更新后再统计左区间对右区间的答案贡献

  但这道题不行

  考虑$dp$方程的正确性,求$dp[i]$的值时,$dp[j](1 \leqslant j < i)$的值都必须是准确的,否则$dp[i]$的值会错,也就是说信息的更新必须是从左至右的,而上面的那种分治过程恰好就改变了$dp$的顺序,本应先用当前序列的左区间更新右区间的值,在用右区间的左区间更新右区间的右区间的值,现在的顺序为先用右区间的左区间更新右区间的右区间的值,再用当前序列的左区间更新右区间的值,这样显然没有满足信息更新的过程必须从左至右这一条件

  因此需要对分治的过程做出改变

  先递归处理左区间,再统计左区间对右区间的贡献,然后递归处理右区间, 类似于中序遍历

  在信息整合时,满足当前区间的左区间的下标值$pos$都小于右区间的下标值$pos$,先对左区间按$mx$值从小到大排序,对右区间按$val$从小到大排序,用双指针法,做一次扫描,设$i$是左区间的指针,$j$是右区间的指针,当$mx[i] \leqslant val[j]$ 时将$dp[i]$在$val[i]$位置插入树状数组,这个树状数组存的不再是前缀和,而是前缀最大值,即最大的$dp$值,而当$val[j] < mx[i]$时,查询$mn[j]$的前缀最大值,$dp[j] = max(dp[j],Ask(mn[j])+1)$

  更新完信息后需要还原现场,将右区间按$pos$从小到大排序才能继续递归右区间

  最后的答案就是$max\left \{ dp[i] \right \}(1 \leqslant i \leqslant n)$

 代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;

inline int read()
{
    int ret, f=1;
    char c;
    while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
    ret=c-'0';
    while((c=getchar())&&(c>='0'&&c<='9'))ret=(ret<<3)+(ret<<1)+c-'0';
    return ret*f;
}

int n, m, ans;

struct info{
    int pos, mx, val, mn, dp;
}a[maxn];

bool cmp1(info x, info y)
{
    return x.mx < y.mx;
}

bool cmp2(info x, info y)
{
    return x.val < y.val;
}

bool cmp3(info x, info y)
{
    return x.pos < y.pos;
}

int lowbit(int x)
{
    return x & -x;
}

int s[maxn];

void Modify(int x, int w)
{
    while(x <= 100000)
    {
        s[x] = max(s[x], w);
        x += lowbit(x);
    }
}

int Ask(int x)
{
    int ret = -1;
    while(x)
    {
        ret = max(s[x], ret);
        x -= lowbit(x);
    }
    return ret;
}

void Clear(int x)
{
    while(x <= 100000)
    {
        s[x] = 0;
        x += lowbit(x);
    }
}

void CDQ(int l, int r)
{
    if(l == r)    return ;
    int mid = (l + r) >> 1;
    CDQ(l, mid);
    sort(a + l, a + mid + 1, cmp1);
    sort(a + mid + 1, a + r + 1, cmp2);
    int i = l, j = mid + 1;
    for(int k = l; k <= r; ++k)
    {
        if((i <= mid && a[i].mx <= a[j].val) || j > r)
        {
            Modify(a[i].val, a[i].dp);
            ++ i;
        }
        else
        {
            a[j].dp = max(a[j].dp, Ask(a[j].mn) + 1);
            ++ j;
        }
    }
    for(int k = l; k <= mid; ++k)
        Clear(a[k].val);
    sort(a + mid + 1, a + r + 1, cmp3);
    CDQ(mid + 1, r);
}

int main()
{
    n = read();m = read();
    for(int i = 1; i <= n; ++i)
    {
        a[i].pos = i;
        a[i].val = read();
        a[i].mx = a[i].val;    
        a[i].mn = a[i].val;
        a[i].dp = 1;
    }
    for(int i = 1; i <= m; ++i)
    {
        int p = read(), tmp = read();
        a[p].mx = max(a[p].mx, tmp); 
        a[p].mn = min(a[p].mn, tmp);    
    }
    CDQ(1, n);
    for(int i = 1; i <= n; ++i)
        ans = max(ans, a[i].dp);
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2019-10-12 09:49  Mr_Joker  阅读(152)  评论(0编辑  收藏  举报