bzoj3343 教主的魔法

来源:互联网 发布:cnc编程软件mastercam 编辑:程序博客网 时间:2024/04/16 12:50

题目链接:bzoj3343
题目大意:
给一个长度为N的数列,在上面进行两种操作:
1、对闭区间 [L, R] 内所有数加上W
2、询问闭区间 [L, R] 内有多少个数大于等于C。

N≤1000000,Q≤3000,1≤W≤1000,1≤C≤1,000,000,000。

题解:
分块
都要省选了。还根本不会分块的我orz
块内排序二分查询(原来是这种套路= =
修改就用个数组存整块的delta
不完整的部分都暴力修改和查询
时间复杂度大概O(Qnlogn)

#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<iostream>#include<algorithm>using namespace std;#define N 1500#define maxn 1000100int qn,n,a[maxn],b[maxn],del[N],w[maxn];int mymin(int x,int y){return (x<y)?x:y;}void sor(int k){    int l=k*qn,r=mymin((k+1)*qn-1,n);    for (int i=l;i<=r;i++)b[i]=a[i];    sort(b+l,b+r+1);}void change(int l,int r,int c){    int L=w[l],R=w[r];    for (int i=L+1;i<R;i++) del[i]+=c;    if (L==R)    {        for (int i=l;i<=r;i++) a[i]+=c;        sor(L);    }else    {        for (int i=l;w[i]==L;i++) a[i]+=c;        for (int i=r;w[i]==R;i--) a[i]+=c;        sor(L);sor(R);    }}int twocut(int l,int r,int c){    int ret=-1,mid;    while (l<=r)    {        mid=(l+r)>>1;        if (b[mid]<c) l=mid+1;        else ret=mid,r=mid-1;    }    return ret;}int query(int l,int r,int c){    int ret=0,L=w[l],R=w[r];    for (int i=L+1;i<R;i++)    {        int ls=twocut(i*qn,(i+1)*qn-1,c-del[i]);        if (ls!=-1) ret+=(i+1)*qn-ls;    }    if (L==R)    {        for (int i=l;i<=r;i++)            if (a[i]+del[L]>=c) ret++;    }else    {        for (int i=l;w[i]==L;i++) if (a[i]+del[L]>=c) ret++;        for (int i=r;w[i]==R;i--) if (a[i]+del[R]>=c) ret++;    }    return ret;}int main(){    //freopen("a.in","r",stdin);    //freopen("a.out","w",stdout);    int m,q,i,l,r,c;char s[5];    scanf("%d%d",&n,&q);    qn=sqrt(n);m=n/qn;    memset(del,0,sizeof(del));    for (i=1;i<=n;i++)    {        scanf("%d",&a[i]);        w[i]=i/qn;    }    scanf("\n");    for (i=0;i<=m;i++) sor(i);    for (i=1;i<=q;i++)    {        scanf("%s%d%d%d",s,&l,&r,&c);        if (s[0]=='M') change(l,r,c);        else printf("%d\n",query(l,r,c));    }    return 0;}
0 0