• BSOJ 5100 简单的区间


    BSOJ 5100 简单的区间

    题意:

     

     看数据范围知复杂度

    对于30%的数据

    可以直接暴力n^2统计

    对于另外的20%

    并不知道怎么做,应该是给了一些基于随机的数据结构的分吧(例ODT,但不一定是这道题)

     对于100%

    考虑分治,我们可以把整个询问区间分成[1,mid]和[mid+1,n]

    不断地分治下去就是[l,mid],[mid+1,r]

    那么对于单点,容易发现f(i,i)不合法,直接返回,此时对答案的贡献为0

    情况1:对于queryl和queryr都在[l,mid]中的区间,我们直接继续分治下去,queryl和queryr在[mid+1,r]当中同样如此(因为分治到最后总能变成情况2)

    情况2:对于queryl属于[l,mid],queryr属于[mid+1,r]:

    我们可以暴力统计:

    1.记录l~mid的后缀和suf与后缀的最大值max1,以及mid+1~r的前缀和pre与前缀最大值max2

    2.枚举i属于l~mid,此时用一个指针qr属于[mid+1,r]来求出:当前最小的可使得max{a[mid+1~qr]}>max{a[i,mid]}的位置,在循环枚举i的时候单调的往后移动就行了,在移动的时候,我们桶记录和为pre[ql]的数量即可

    然后在枚举i的时候,对于每一个位置i,我们都在桶里面找到一个和suf[i]-max1[i]相加可以被k整除的pre[w](具体实现见代码),此时就让ans加上贡献,即这样的pre[w]的个数(在桶里O(1)询问即可)

    3.枚举i属于mid+1~r,过程和上述方式一样

    其实上面步骤2,3本质就是钦定了某个数作为了全局的max值(这个是动态的),而且二分的过程钦定了整个区间的sum,则我们可以做到统计这个sum

    最后注意%k的处理即可

    那么代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
        x=0;char ch=getchar();bool f=false;
        while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x=f?-x:x;
        return;
    }
    template <typename T>
    inline void write(T x){
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    const int N=2000005;
    int n,k,a[N],pre[N],suf[N],t[N],max1[N],max2[N],ans;
    void solve(int l,int r){
        if(l==r) return ;
        int mid=(l+r)>>1;
        pre[mid]=suf[mid+1]=max1[mid+1]=max2[mid]=0;
        for(int i=mid;i>=l;i--){
            suf[i]=(suf[i+1]+a[i])%k;
            max1[i]=max(max1[i+1],a[i]);
        }
        for(int i=mid+1;i<=r;i++){
            pre[i]=(pre[i-1]+a[i])%k;
            max2[i]=max(max2[i-1],a[i]);
        }
        int ql=mid,qr=mid+1;
        for(int i=mid;i>=l;i--){
            while(qr<=r&&max2[qr]<=max1[i]) t[pre[qr]]++,qr++;
            ans+=t[(k-(suf[i]-max1[i]+k)%k)%k];
        }
        for(int i=mid+1;i<qr;i++) t[pre[i]]--;
        for(int i=mid+1;i<=r;i++){
            while(ql>=l&&max1[ql]<max2[i]) t[suf[ql]]++,ql--;
            ans+=t[(k-(pre[i]-max2[i]+k)%k)%k];
        }
        for(int i=mid;i>ql;i--) t[suf[i]]--;
        solve(l,mid);
        solve(mid+1,r);
        return ;
    }
    int main(){
        read(n),read(k);
        for(int i=1;i<=n;i++) read(a[i]);
        solve(1,n);
        write(ans);
        return 0;
    }
    View Code

      

  • 相关阅读:
    【HDU2050】折线分割平面
    【Codevs1183】泥泞的道路
    Pair
    【Poj 1832】连环锁
    【Poj1090】Chain
    【UVa 10881】Piotr's Ants
    【Codeforces】665E Beautiful Subarrays
    【T^T】【周赛】第一周周赛——欢迎16级的新同学
    【OI新闻】2016.10.09
    二分图的最大匹配
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14074427.html
Copyright © 2020-2023  润新知