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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值