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

  

原文地址:https://www.cnblogs.com/Akmaey/p/14074427.html