cdq分治——bzoj4553: [Tjoi2016&Heoi2016]序列

http://www.lydsy.com/JudgeOnline/problem.php?id=4553

先递归处理[l,mid]
对于所有在[l,mid]中的修改操作,处理它对于所有在[mid+1,r]中的询问操作的影响
再递归处理[mid+1,r]

这个就是cdq;
题意就是对于i>j
max[j]<=a[i]
a[j]<=min[i]
我们二分区间;
对于区间l~r
先处理l~mid;
然后考虑两个小区间的影响;
然后在处理mid+1~r;
对于影响,其实就是用已经处理好的
f[i]去更新未处理好的f[i];
f[i]就是以i为结束点最长串;
递归好l~mid的时候f[i](i <= l&&i < =mid)是处理好的;
为了满足上面的两个条件,我们这样;
把l~mid之间的数按max排序;
mid+1和r之间按a排序;
然后我们就可以用一个下标指针在线性的时间里维护第一个条件;
对于第二个条件我们只要维护一个权值树状数组就好了;
只要在线性维护第一个条件的时候不断把前面的小区间里的数加进树状数组;
然后用了多少就清空多少,这样省时间;
时间复杂度是n*log(n)^2;

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Ll long long
using namespace std;
const int N=1e5+5;
struct cs{int a,ma,mi,num;}a[N],c[N];
int f[N],F[N];
int n,m,x,y,ans;
bool cmp1(cs x,cs y){return x.ma<y.ma;}
bool cmp2(cs x,cs y){return x.a<y.a;}
int outit(int i){
    int ans=0;
    for(;i;i-=i&-i)ans=max(F[i],ans);
    return ans;
}
void init(int i,int y){for(;i<=N;i+=i&-i)F[i]=max(F[i],y);}
void cle(int i){for(;i<=N;i+=i&-i)F[i]=0;}
void cdq(int l,int r){
    if(l==r){f[1]=max(f[1],1);ans=max(f[l],ans);return;}
    int mid=(l+r)/2;
    cdq(l,mid);
    for(int i=l;i<=r;i++)c[i]=a[i];
    sort(c+l,c+mid+1,cmp1);
    sort(c+mid+1,c+r+1,cmp2);
    int k=l-1;
    for(int i=mid+1;i<=r;i++){
        while(k<mid&&c[k+1].ma<=c[i].a){k++;init(c[k].a,f[c[k].num]);}
        f[c[i].num]=max(f[c[i].num],outit(c[i].mi)+1);
    }
    for(int i=l;i<=mid;i++)cle(c[i].a);
    cdq(mid+1,r);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i].a),a[i].ma=a[i].mi=a[i].a,a[i].num=i;
    while(m--){
        scanf("%d%d",&x,&y);
        a[x].ma=max(a[x].ma,y);
        a[x].mi=min(a[x].mi,y);
    }
    cdq(1,n);
    printf("%d",ans);
}
posted @ 2017-04-21 13:57  largecube233  阅读(118)  评论(0编辑  收藏  举报