玄学算法CDQ分治

啊刚学这个啊
刚过了一道题就屁颠屁颠来写博客很虚啊
bzoj4553
就是这道题

说一下我对CDQ分治的理解
我感觉。。这个就类似于。。把暴力,转化为。。容易优化的暴力。。然后优化??

并且一般只用于处理问题具有单调性的题,即f[i]对任意f[j](j={1..i},不产生影响。

例如最长上升子序列的问题

我们本来需要枚举这个j,但是通过CDQ分治就不需要去全部枚举了


我们处理1~n的时候,如果1~n/2已经全部处理完毕,那么就可以用1~n/2的结果去更新n/2+1~n的结果

注意 这里是更新,而不是算出
因为等会儿我们会枚举n/2+1~n/2+n/4去更新n/2+n/4+1~n

这样,f[i]就会被所有比他小的点更新

这里仍然举最长上升子序列的例子,暴力dp显然是n^2,然而上面说的这个想法,如果不加任何处理。。也是n^2,甚至常数更大

但是我们可以对前面的值进行一些处理,比如按照a[i]排序。

同时对后面的值处理,那么我们就可以线性推,把一次更新的时间优化到2(r-l),整个时间效率也就优化到了n*log n


那么再来看看看看一开始说的那道题,其实我们只要
a[i]
就可以类似于最长上升子序列一样n^2的dp了,其中min,max指j这个位置最小和最大可能变成的值

正确性显然

那么考虑CDQ分治

这次有二维的条件,显然一个排序后仍然不能确保前面的值全部可以取,那么怎么办呢

排序后显然只有一维的条件了,我们再套一个CDQ我们可以用树状数组,记录权值的前缀和就可以了

自己也不太懂啊就这样吧

CODE:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 1e9
#define ll long long
#define For(i,j,k) for(ll i=j;i<=k;i++)
#define Dow(i,j,k) for(ll i=k;i>=j;i--)
using namespace std;
int n,m,a[100001],f[100001],ff[100001],mi[100001],mx[100001],num[100001];
inline bool cmp1(int x,int y)
{
    return mi[x]inline bool cmp2(int x,int y)
{
    return a[x]inline int lowbit(int x){return x&-x;}
void add(int x,int v)
{
    for(int i=x;i<=100001;i+=lowbit(i)) if (v!=0)ff[i]=max(ff[i],v);else ff[i]=0;   
}
int get(int x)
{
    int sum=0;
    for(int i=x;i;i-=lowbit(i)) sum=max(sum,ff[i]);
    return sum;
}
void solve (int l,int r)
{
    if(l==r)
    {
        f[l]=max(f[l],1);
        return;
    }   
    int mid=(l+r)>>1;
    solve(l,mid);
    For(i,l,r)  num[i]=i;
    sort(num+l,num+mid+1,cmp2);sort(num+mid+1,num+r+1,cmp1);
    int poi=l;
    For(i,mid+1,r)
    {
        while(a[num[poi]]<=mi[num[i]]&&poi<=mid)
            add(mx[num[poi]],f[num[poi]]),poi++;
        f[num[i]]=max(f[num[i]],get(a[num[i]])+1);
    }
    For(i,l,poi)    add(mx[num[i]],0);
    solve(mid+1,r);
}
int main()
{
    scanf("%d%d",&n,&m);
    For(i,1,n)  scanf("%d",&a[i]),mx[i]=mi[i]=a[i];
    For(i,1,m)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        mx[x]=max(mx[x],y);
        mi[x]=min(mi[x],y);
    }
    solve(1,n);
    int ans=0;
    For(i,1,n)  ans=max(ans,f[i]);
    printf("%d\n",ans);
}

你可能感兴趣的:(算法)