• 分块 莫队


    学习了一下很基本的分块和莫队算法,因为不太会写曼哈顿距离最小生成树,所以就写了个分块版本的(分四种情况,大概这个意思吧)。。。

    分块

    bzoj2957 楼房重建

    题目大意:在n的区间上,某一点可以升高或降低,求从(0,0)能看到的楼的栋数(只能看到顶上一点时不算看到)。

    思路:其实就是求一个点i,使得i前面所有点的斜率都小于这个点,所以分块搞一下,保存一个块内从左到右单增的序列,查询的时候每个块二分一下就可以了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define maxm 100005
    using namespace std;
    int len,block,edge[maxm][2]={0},num[maxm]={0};
    double hi[maxm]={0},ai[1000][1000]={0};
    void tch(int x){
        int i,j;double maxn=0.0;num[x]=0;
        for (i=edge[x][0];i<=edge[x][1];++i)
            if (hi[i]>maxn) maxn=ai[x][++num[x]]=hi[i];
    }
    int work(){
        int i,j,ans=0;double maxn=0.0;
        for (i=1;i<=block;++i){
            j=upper_bound(ai[i]+1,ai[i]+num[i]+1,maxn)-ai[i]-1;
            ans+=num[i]-j;maxn=max(maxn,ai[i][num[i]]);
        }return ans;
    }
    int main(){
        int n,m,i,j,x,y;scanf("%d%d",&n,&m);
        len=(int)sqrt(n);block=(n+len-1)/len;
        for (i=1;i<=block;++i){edge[i][0]=(i-1)*len+1;edge[i][1]=min(i*len,n);}
        for (i=1;i<=m;++i){
            scanf("%d%d",&x,&y);hi[x]=(double)y*1.0/(double)x;
            tch((x+len-1)/len);printf("%d
    ",work());
        }
    }
    View Code

    bzoj4320 homework

    题目大意:每次添加一个数x,查询求%y的最小值。

    思路:对于y<根号300000的数,可以用数组维护一个答案;对于y>根号300000的,可以扫每一个ky~(k+1)y的区间,然后用线段树找出位置最靠前的,然后更新答案。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxm 300005
    #define up 600
    #define ji 300000
    using namespace std;
    int ans[up+5]={0},maxn,t[maxm<<2]={0};
    char in(){
        char ch=getchar();
        while(ch<'A'||ch>'Z') ch=getchar();
        return ch;
    }
    int sc(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;
    }
    void ins(int i,int l,int r,int x){
        if (l==r){t[i]=l;return;}
        int mid=(l+r)>>1;
        if (x<=mid) ins(i<<1,l,mid,x);
        else ins(i<<1|1,mid+1,r,x);
        if (t[i<<1]) t[i]=t[i<<1];
        else t[i]=t[i<<1|1];
    }
    void task(int i,int l,int r,int ll,int rr){
        if (ll<=l&&r<=rr){
            if (t[i]) maxn=min(maxn,t[i]);
            return;
        }int mid=(l+r)>>1;
        if (ll<=mid) task(i<<1,l,mid,ll,rr);
        if (rr>mid) task(i<<1|1,mid+1,r,ll,rr);
    }
    int main(){
        int i,j,n,minn,x;char ch;
        scanf("%d",&n);memset(ans,127,sizeof(ans));
        for (i=1;i<=n;++i){
            ch=in();x=sc();
            if (ch=='A'){
                for (j=1;j<=up;++j) ans[j]=min(ans[j],x%j);
                ins(1,1,ji,x);
            }else{
                if (x<=up) printf("%d
    ",ans[x]);
                else{
                    for(minn=ans[0],j=0;;){
                        maxn=ans[0];task(1,1,ji,max(j,1),min(j+x-1,ji));
                        if (maxn-j<minn) minn=maxn-j;
                        j+=x;
                        if (j>ji||!minn) break;
                    }printf("%d
    ",minn);
                }
            }
        }
    }
    View Code

    bzoj3744 Gty的妹子序列

    题目大意:已知数列ai,求l~r的逆序对个数。

    思路:分块。预处理l~r块的逆序对个数和前i个块的<x的个数和,查询的时候再用树状数组做一下首尾不全的部分。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define N 50005
    #define M 305
    using namespace std;
    int ai[N],bi[N],ci[N],cz=0,cc[M][N]={0},be[N]={0},kz=0,sz,gi[M][M]={0},lp[M]={0};
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    int lowbit(int x){return x&(-x);}
    void cins(int x,int y){for (;x<=cz;x+=lowbit(x)) ci[x]+=y;}
    int cask(int x){int sm=0;for (;x;x-=lowbit(x)) sm+=ci[x];return sm;}
    int main(){
        int n,m,i,j,k,cj,la=0,lb,rb,l,r,ji;n=in();
        for (i=1;i<=n;++i) ai[i]=bi[i]=in();
        sort(bi+1,bi+n+1);
        cz=unique(bi+1,bi+n+1)-bi-1;
        sz=sqrt(n);kz=(n-1)/sz+1;
        for (i=1;i<=n;++i){
            ai[i]=upper_bound(bi+1,bi+cz+1,ai[i])-bi-1;
            cj=be[i]=(i-1)/sz+1;
            if (be[i]!=be[i-1]) memset(ci,0,sizeof(ci));
            lp[cj]=i;
            for (j=cj;j<=kz;++j) ++cc[j][ai[i]];
            cins(ai[i],1);
            gi[cj][cj]+=i-lp[cj-1]-cask(ai[i]);
        }for (i=1;i<=kz;++i)
            for (j=2;j<=cz;++j) cc[i][j]+=cc[i][j-1];
        for (i=1;i<=kz;++i){
            for (j=i+1;j<=kz;++j){
                gi[i][j]=gi[i][j-1]+gi[j][j];
                for (k=lp[j-1]+1;k<=lp[j];++k)
                    gi[i][j]+=((lp[j-1]-lp[i-1])-(cc[j-1][ai[k]]-cc[i-1][ai[k]]));
            }
        }la=0;m=in();
        for (i=1;i<=m;++i){
            memset(ci,0,sizeof(ci));
            l=in()^la;r=in()^la;
            if (l>r) swap(l,r);
            lb=be[l];rb=be[r];
            la=gi[lb+1][rb-1];
            if (lb==rb)
                for (j=l;j<=r;++j){
                    cins(ai[j],1);la+=j-l+1-cask(ai[j]);
                }
            else{
                ji=lp[rb-1]-lp[lb];
                for (j=l;be[j]==lb;++j){
                    cins(ai[j],1);
                    la+=j-l+1-cask(ai[j])+cc[rb-1][ai[j]-1]-cc[lb][ai[j]-1];
                }for (j=lp[rb-1]+1;j<=r;++j){
                    cins(ai[j],1);
                    la+=(lp[lb]-l+1+j-lp[rb-1])-cask(ai[j])+
                        (!ji ? 0 : ji-(cc[rb-1][ai[j]]-cc[lb][ai[j]]));
                }
            }printf("%d
    ",la);
        }
    }
    View Code

    莫队

    cogs1775||bzoj2038 小Z的袜子

    题目大意:静态区间查询不同种元素的个数。

    思路:用莫队扫一下,然后分子分母同时乘2,就会发现,分母是组合数化简后的(r-l)*(r-l+1),分子是每种颜色个数的平方-区间元素(从相同颜色的当中选两个可重的,去掉两个是一个的),然后就很好处理了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct use{
        long long zi,mu;
    }ans[50001]={0};
    struct used{
        int p,ll,rr;
    }ask[50001]={0};
    int a[50001]={0},num[50001]={0},len;
    int my_comp(const used &x,const used &y)
    {
        if (x.ll/len<y.ll/len) return 1;
        if (x.ll/len==y.ll/len&&x.rr<=y.rr) return 1;
        return 0;
    }
    long long gcd(long long x,long long y)
    {
        if (x%y==0) return y;
        else return gcd(y,x%y);
    }
    int main()
    {
        freopen("hose.in","r",stdin);
        freopen("hose.out","w",stdout);
        
        int n,m,i,j,block,l,r;
        long long sum;
        scanf("%d%d",&n,&m);
        len=floor(sqrt(n));
        for (i=1;i<=n;++i) scanf("%d",&a[i]);
        for (i=1;i<=m;++i)
        {
            ask[i].p=i;
            scanf("%d%d",&ask[i].ll,&ask[i].rr);
        }
        sort(ask+1,ask+m+1,my_comp);
        l=1;r=0;sum=0;
        for (i=1;i<=m;++i)
        {
            while(r<ask[i].rr)
            {
                ++r;sum-=(long long)num[a[r]]*num[a[r]];
                ++num[a[r]];sum+=(long long)num[a[r]]*num[a[r]];
            }
            while(r>ask[i].rr)
            {
                sum-=(long long)num[a[r]]*num[a[r]];--num[a[r]];
                sum+=(long long)num[a[r]]*num[a[r]];--r;
            }
            while(l<ask[i].ll)
            {
                sum-=(long long)num[a[l]]*num[a[l]];--num[a[l]];
                sum+=(long long)num[a[l]]*num[a[l]];++l;
            }
            while(l>ask[i].ll)
            {
                --l;sum-=(long long)num[a[l]]*num[a[l]];
                ++num[a[l]];sum+=(long long)num[a[l]]*num[a[l]];
            }
            ans[ask[i].p].zi=sum-(ask[i].rr-ask[i].ll+1);
            ans[ask[i].p].mu=(long long)(ask[i].rr-ask[i].ll+1)*(ask[i].rr-ask[i].ll);
        }
        for (i=1;i<=m;++i)
        {
            if (ans[i].zi!=0)
            {
              sum=gcd(ans[i].zi,ans[i].mu);
              printf("%lld/%lld
    ",ans[i].zi/sum,ans[i].mu/sum);
            }
            else printf("0/1
    ");
        }
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    cogs1822||bzoj3236 作业

    题目大意:静态查询区间内数字∈[a,b]的个数和数字的种数。

    思路:同上,用权值树状数组维护一下就可以了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct use{
        int ll,rr,aa,b,p,kk;
    }ask[1000001]={0};
    int c1[100001]={0},c2[100001]={0},n,a[100001],len,ans[1000001][2]={0},visit[100001]={0};
    int lowbit(int x) {return x&(-x);}
    int my_comp(const use &x,const use &y)
    {
        if (x.kk<y.kk) return 1;
        if (x.kk==y.kk&&x.rr<y.rr) return 1;
        return 0;
    }
    int bit_ask(int kind,int l,int r)
    {
        int sum=0;
        --l;
        if (kind==1)
        {
          for (;r;r-=lowbit(r)) sum+=c1[r];
          for (;l;l-=lowbit(l)) sum-=c1[l];
        }
        else
        {
          for (;r;r-=lowbit(r)) sum+=c2[r];
          for (;l;l-=lowbit(l)) sum-=c2[l];
        }
        return sum;
    }
    void bit_ins(int x)
    {
        int t;t=x;
        for (;t<=n;t+=lowbit(t)) c1[t]+=1;
        if (!visit[x])
          for (;x<=n;x+=lowbit(x)) c2[x]+=1;
    }
    void bit_del(int x)
    {
        int t;t=x;
        for (;t<=n;t+=lowbit(t)) c1[t]-=1;
        if (!visit[x])
          for (;x<=n;x+=lowbit(x)) c2[x]-=1;
    }
    int main()
    {
        freopen("ahoi2013_homework.in","r",stdin);
        freopen("ahoi2013_homework.out","w",stdout);
        
        int m,i,j,l,r;
        scanf("%d%d",&n,&m);
        len=floor(sqrt(n));
        for (i=1;i<=n;++i) scanf("%d",&a[i]);
        for (i=1;i<=m;++i) 
        {
            ask[i].p=i;
            scanf("%d%d%d%d",&ask[i].ll,&ask[i].rr,&ask[i].aa,&ask[i].b);
            ask[i].kk=ask[i].ll/len+1;
        }
        sort(ask+1,ask+m+1,my_comp);
        l=1;r=0;
        for (i=1;i<=m;++i)
        {
            while(r<ask[i].rr)
            {
                ++r;bit_ins(a[r]);++visit[a[r]];
            }
            while(r>ask[i].rr)
            {
                --visit[a[r]];bit_del(a[r]);--r;
            }
            while(l<ask[i].ll)
            {
                --visit[a[l]];bit_del(a[l]);++l;
            }
            while(l>ask[i].ll)
            {
                --l;bit_ins(a[l]);++visit[a[l]];
            }
            ans[ask[i].p][0]=bit_ask(1,ask[i].aa,ask[i].b);
            ans[ask[i].p][1]=bit_ask(2,ask[i].aa,ask[i].b);
        }
        for (i=1;i<=m;++i) printf("%d %d
    ",ans[i][0],ans[i][1]);
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    cogs1689||bzoj2002 Bounce 弹飞绵羊

    题目大意:有n个弹力装置,每一个装置有一个值,能想后弹射k个单位(即从i弹到i+k的位置);两种操作:1是询问从i到弹飞用多少步,2是修改某个弹力装置的值。

    思路:分块之后,对于每个点都保存从这个点到弹出这个块的次数,以及弹出后到的点,查询的时候√n,修改的时候只改一个块的一部分,所以也是√n。O(m√n)本来认为过不了的,没想到数据水,就这样吧。。。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int a[200001]={0},ku[500][2]={0},kuai[500][500]={0},f[200001][2]={0};
    int main()
    {
        freopen("bzoj_2002.in","r",stdin);
        freopen("bzoj_2002.out","w",stdout);
        
        int i,j,k,n,m,len,block,ans;
        scanf("%d",&n);
        len=floor(sqrt(n));
        if (n%len==0) block=n/len;
        else block=n/len+1;
        for (i=1;i<=block;++i)
        {
            ku[i][0]=len*(i-1)+1;
            ku[i][1]=min(n,len*i);
        }
        for (i=1;i<=n;++i) scanf("%d",&a[i]);
        for (i=1;i<=block;++i)
          for (j=ku[i][1];j>=ku[i][0];--j)
          {
                if (j+a[j]>ku[i][1])
                {
                    f[j][0]=1;f[j][1]=j+a[j];
                }
                else
                {
                    f[j][0]=f[j+a[j]][0]+1;f[j][1]=f[j+a[j]][1];
                }
          }
        scanf("%d",&m);++m;
        while(--m)
        {
            scanf("%d%d",&i,&j);
            ++j;
            if (i==1)
            {
                ans=0;
                for (;j<=n;j=f[j][1]) ans+=f[j][0];
                printf("%d
    ",ans);
            }
            else
            {
                scanf("%d",&k);
                i=(j-1)/len+1;a[j]=k;
                for (;j>=ku[i][0];--j)
                {
                    if (j+a[j]>ku[i][1])
                    {
                        f[j][0]=1;f[j][1]=j+a[j];
                    }
                    else
                    {
                        f[j][0]=f[j+a[j]][0]+1;f[j][1]=f[j+a[j]][1];
                    }
                }
            }
        }
        
        fclose(stdin);
        fclose(stdout);
    }
    View Code

    bzoj4358 permu

    题目大意:给定一个n的排列,求不同区间内连续权值的最大长度。

    思路:离线莫队+线段树是第一反应,但是因为多了一个log所以会tle。可以用莫队的思想做右端点,用并查集维护一段连续的数的个数(相当于并查集的siz),更新答案;然后暴力处理左端点,用另一个并查集维护这些数,保存每个数所在连续段的左右端点,更新答案。

    注意:1)这里并查集的时候一开始都赋成0,后来找的时候更新一下就行了;

            2)第二个并查集不能每次都memset,而是用一个栈维护一下,要把所有fa改过的值都放入栈里。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define N 50005
    using namespace std;
    struct use{int l,r,p;}bi[N]={0};
    int ai[N],fa[2][N],zh[N*3],siz[N],li[N],ri[N],en[N],po[N],mn,tot=0,ans[N];
    int cmp(const use&x,const use&y){return (po[x.l]==po[y.l] ? x.r<y.r : x.l<y.l);}
    int root(int k,int x){
        if (fa[k][x]==x) return x;
        return fa[k][x]=root(k,fa[k][x]);}
    void add(int x){
        int v;siz[x]=1;fa[0][x]=x;
        if (v=root(0,x-1)){fa[0][v]=x;siz[x]+=siz[v];}
        if (v=root(0,x+1)){fa[0][v]=x;siz[x]+=siz[v];}
        mn=max(mn,siz[x]);}
    void add2(int x,int i){
        int v;li[x]=ri[x]=fa[1][x]=x;zh[++tot]=x;
        if (v=root(1,x-1)) li[x]=li[v];
        else if (v=root(0,x-1)) li[x]-=siz[v];
        if (v=root(1,x+1)) ri[x]=ri[v];
        else if (v=root(0,x+1)) ri[x]+=siz[v];
        zh[++tot]=li[x];zh[++tot]=ri[x];
        fa[1][li[x]]=fa[1][ri[x]]=x;
        ans[i]=max(ans[i],ri[x]-li[x]+1);
    }
    int main(){
        int i,j,n,m,cur,k;scanf("%d%d",&n,&m);
        int len=sqrt(n);
        for (i=1;i<=n;++i){
            scanf("%d",&ai[i]);en[po[i]=(i-1)/len+1]=i;
        }for (i=1;i<=m;++i){
            scanf("%d%d",&bi[i].l,&bi[i].r);bi[i].p=i;
        }sort(bi+1,bi+m+1,cmp);
        for (i=1;i<=m;){
            j=i;while(j<m&&po[bi[j+1].l]==po[bi[i].l]) ++j;
            memset(fa[0],0,sizeof(fa[0]));
            cur=en[po[bi[i].l]];mn=0;
            while(i<=j){
                while(cur<bi[i].r) add(ai[++cur]);
                ans[bi[i].p]=mn;
                for (k=min(bi[i].r,en[po[bi[i].l]]);k>=bi[i].l;--k)
                  add2(ai[k],bi[i].p);
                while(tot) fa[1][zh[tot--]]=0;
                ++i;
            }
        }for (i=1;i<=m;++i) printf("%d
    ",ans[i]);
    }
    View Code

    bzoj4540 序列

    题目大意:给一个长度为n的序列,q次询问:l~r的子序列的最小值之和。

    思路:莫队。考虑每次r-1 -> r,能增加贡献的是r之前单调增的一些数字,这些中的每个数字ai[i]能贡献ai[i]*(i-pre[i])(pre[i]表示i之前的<=i的最后一个数字),其中最小的那个贡献的是ai[i]*(i-l+1),要O(1)转移,所以可以做一个类似前缀和,对于求最小值可以用st表。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define N 100005
    #define up 20
    #define LL long long
    using namespace std;
    int in(){
        char ch=getchar();int x=0,f=1;
        while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-'){f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x*f;}
    LL ai[N],ans[N],psm[N]={0LL},ssm[N]={0LL};
    int be[N],pre[N],suc[N],tr[N<<2],n,lo[N],pgn[N][up],sgn[N][up];
    struct uu{LL x;int po;}di[N];
    int cmp1(const uu&x,const uu&y){return (x.x==y.x ? x.po<y.po : x.x<y.x);}
    int cmp2(const uu&x,const uu&y){return (x.x==y.x ? x.po>y.po : x.x<y.x);}
    struct use{
        int l,r,po;
        bool operator<(const use&x)const{
            return (be[l]==be[x.l] ? r<x.r : be[l]<be[x.l]);
        }
    }qi[N];
    int amx(int i,int l,int r,int ll,int rr){
        if (ll<=l&&r<=rr) return tr[i];
        int mx=0,mid=(l+r)>>1;
        if (ll<=mid) mx=max(mx,amx(i<<1,l,mid,ll,rr));
        if (rr>mid) mx=max(mx,amx(i<<1|1,mid+1,r,ll,rr));
        return mx;}
    int amn(int i,int l,int r,int ll,int rr){
        if (ll<=l&&r<=rr) return tr[i];
        int mn,mid=(l+r)>>1;mn=n+1;
        if (ll<=mid) mn=min(mn,amn(i<<1,l,mid,ll,rr));
        if (rr>mid) mn=min(mn,amn(i<<1|1,mid+1,r,ll,rr));
        return mn;}
    void tch(int i,int l,int r,int x,int y,int z){
        if (l==r){tr[i]=y;return;}
        int mid=(l+r)>>1;
        if (x<=mid) tch(i<<1,l,mid,x,y,z);
        else tch(i<<1|1,mid+1,r,x,y,z);
        if (z) tr[i]=max(tr[i<<1],tr[i<<1|1]);
        else tr[i]=min(tr[i<<1],tr[i<<1|1]);
    }
    void pree(){
        int i,j,k,ji;
        sort(di+1,di+n+1,cmp1);
        memset(tr,0,sizeof(tr));
        for (j=0,i=1;i<=n;++i){
            k=di[i].po;
            pre[k]=amx(1,1,n,1,k);
            psm[k]=psm[pre[k]]+(LL)(k-pre[k])*di[i].x;
            tch(1,1,n,k,k,1);
            if ((1<<(j+1))<=i) ++j;
            lo[i]=j;
        }sort(di+1,di+n+1,cmp2);
        for (i=(N<<2)-1;i;--i) tr[i]=n+1;
        for (i=1;i<=n;++i){
            k=di[i].po;
            suc[k]=amn(1,1,n,k,n);
            ssm[k]=ssm[suc[k]]+(LL)(suc[k]-k)*di[i].x;
            tch(1,1,n,k,k,0);
            pgn[i][0]=sgn[i][0]=i;
        }for (i=1;i<up;++i)
            for (ji=1<<i,j=1;j+ji-1<=n;++j){
                if (ai[pgn[j][i-1]]<=ai[pgn[j+(ji>>1)][i-1]]) pgn[j][i]=pgn[j][i-1];
                else pgn[j][i]=pgn[j+(ji>>1)][i-1];
                if (ai[sgn[j+(ji>>1)][i-1]]<=ai[sgn[j][i-1]]) sgn[j][i]=sgn[j+(ji>>1)][i-1];
                else sgn[j][i]=sgn[j][i-1];
            }
    }
    int gpn(int l,int r){
        int x=lo[r-l+1];
        if (ai[pgn[l][x]]<=ai[pgn[r-(1<<x)+1][x]]) return pgn[l][x];
        else return pgn[r-(1<<x)+1][x];
    }
    int gsn(int l,int r){
        int x=lo[r-l+1];
        if (ai[sgn[r-(1<<x)+1][x]]<=ai[sgn[l][x]]) return sgn[r-(1<<x)+1][x];
        else return sgn[l][x];
    }
    int main(){
        int q,i,l,r,kz,pm;LL cnt=0LL;
        n=in();q=in();kz=sqrt(n);
        for (i=1;i<=n;++i){
            ai[i]=(LL)in();
            di[i]=(uu){ai[i],i};
            be[i]=(i-1)/kz+1;
        }pree();
        for (i=1;i<=q;++i) qi[i]=(use){in(),in(),i};
        sort(qi+1,qi+q+1);
        for (l=i=1,r=0;i<=q;++i){
            while(r<qi[i].r){
                ++r;pm=gpn(l,r);
                cnt+=psm[r]-psm[pm]+(LL)(pm-l+1)*ai[pm];
            }while(r>qi[i].r){
                pm=gpn(l,r);
                cnt-=psm[r]-psm[pm]+(LL)(pm-l+1)*ai[pm];
                --r;
            }while(l<qi[i].l){
                pm=gsn(l,r);
                cnt-=ssm[l]-ssm[pm]+(LL)(r-pm+1)*ai[pm];
                ++l;
            }while(l>qi[i].l){
                --l;pm=gsn(l,r);
                cnt+=ssm[l]-ssm[pm]+(LL)(r-pm+1)*ai[pm];
            }ans[qi[i].po]=cnt;
        }for (i=1;i<=q;++i) printf("%I64d
    ",ans[i]);
    }
    View Code

    这题还有一种做法:同平衡二叉树treap中省队集训day2T3的解法二。 

    bzoj4542 大数

    题目大意:给定一个数字串,m组询问,求l~r的子串中%p=0的串的个数。

    思路:莫队,利用后缀的数字的话,一段%p=0就是(sum[x]-sum[i+1])/10^(n-i)=0,对于p不是2或5的时候,可以判断sum[x]=sum[i+1];特判p=2或5的情况(以r来说,r的位置是2或5就可以给答案贡献长度,l的时候考虑结尾是2或5的位置个数就可以了)。

    注意:sum[x]=sum[i+1]的情况中的下标。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define N 100005
    #define LL long long
    using namespace std;
    LL p,ai[N],ans[N],cc[N],ma[N]={0LL};
    int be[N],cz,bi[N];
    char ss[N];
    struct use{
        int l,r,p;
        bool operator<(const use&x)const{return (be[l]==be[x.l] ? r<x.r : be[l]<be[x.l]);}
    }qu[N];
    int main(){
        int i,n,m,kz,l,r;LL ci,cnt,x;
        scanf("%I64d%s",&p,ss);
        bool f=(p==2LL||p==5LL);
        n=strlen(ss);kz=sqrt(n);
        for (i=1;i<=n;++i){
            ai[i]=bi[i]=(LL)(ss[i-1]-'0');
            be[i]=(i-1)/kz+1;
        }if (!f){
            cc[cz=1]=ai[n+1]=0LL;
            for (x=1LL,i=n;i;--i){
                ai[i]=(ai[i+1]+ai[i]*x%p)%p;
                x=x*10LL%p;cc[++cz]=ai[i];
            }sort(cc+1,cc+cz+1);
            cz=unique(cc+1,cc+cz+1)-cc-1;
            for (i=1;i<=n+1;++i)
                bi[i]=upper_bound(cc+1,cc+cz+1,ai[i])-cc-1;
        }scanf("%d",&m);
        for (i=1;i<=m;++i){
            scanf("%d%d",&qu[i].l,&qu[i].r);
            qu[i].p=i;
        }sort(qu+1,qu+m+1);
        ++ma[bi[1]];l=1;r=0;ci=cnt=0LL;
        for (i=1;i<=m;++i){
            while(r<qu[i].r){
                ++r;
                if (f){
                    if (ai[r]%p==0LL){++cnt;ci+=(LL)(r-l+1);}
                }else{ci+=ma[bi[r+1]];++ma[bi[r+1]];}
            }while(r>qu[i].r){
                if (f){
                    if (ai[r]%p==0LL){--cnt;ci-=(LL)(r-l+1);}
                }else{--ma[bi[r+1]];ci-=ma[bi[r+1]];}
                --r;
            }while(l<qu[i].l){
                if (f){
                    ci-=cnt;
                    if (ai[l]%p==0LL) --cnt;
                }else{--ma[bi[l]];ci-=ma[bi[l]];}
                ++l;
            }while(l>qu[i].l){
                --l;
                if (f){
                    if (ai[l]%p==0LL) ++cnt;
                    ci+=cnt;
                }else{ci+=ma[bi[l]];++ma[bi[l]];}
            }ans[qu[i].p]=ci;
        }for (i=1;i<=m;++i) printf("%I64d
    ",ans[i]);
    }
    View Code

    树分块

    bzoj3720 Gty的妹子树

    题目大意:支持在线:(1)查u子树中大于x的个数;(2)把u的权值变为x;(3)新加一个点父亲是u,权值是x。

    思路:树上分块,按dfs分块(可能被卡),如果一个点父亲的块的大小满了就新建一个块并把这两个块连边。改的时候暴力改(循环,不要排序)。查询的时候先扫根所在的块的那些孩子,再做其他块。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define N 60005
    #define M 120005
    #define up 300
    using namespace std;
    int point[N]={0},next[M],en[M],tot=0,anc[N],ka[N][up]={0},cnt[N]={0},wi[N],fa[N],
        sz,kz=0,que[N],be[N]={0},po[N]={0},ne[N],vz[N]={0},vt=0;
    bool vi[N]={false};
    int cmp(int x,int y){return wi[x]<wi[y];}
    void add(int u,int v){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;}
    void add1(int u,int v){ne[v]=po[u];po[u]=v;}
    int gete(int u,int x){
        int l,r,mid;
        if (wi[ka[u][cnt[u]]]<=x) return cnt[u]+1;
        l=1;r=cnt[u];
        while(l!=r){
            mid=(l+r)>>1;
            if (wi[ka[u][mid]]>x)r=mid;
            else l=mid+1;
        }return l;}
    void ins(int u,int x){
        int i=cnt[u];
        for (;i>1&&wi[ka[u][i-1]]>wi[x];--i)
            ka[u][i]=ka[u][i-1];
        ka[u][i]=x;}
    void tch(int u,int x,int v){
        int i,j;i=gete(u,wi[x]-1);wi[x]=v;
        for (;i<cnt[u]&&wi[ka[u][i+1]]<v;++i)
            ka[u][i]=ka[u][i+1];
        for (;i>1&&wi[ka[u][i-1]]>v;--i)
            ka[u][i]=ka[u][i-1];
        ka[u][i]=x;}
    void adka(int u,int v){
        if (cnt[be[u]]<sz){
            be[v]=be[u];anc[v]=anc[u];
        }else{
            be[v]=++kz;anc[v]=v;
            add1(be[u],be[v]);
        }ka[be[v]][++cnt[be[v]]]=v;
    }
    void dfs(int u,int ff){
        int i,v;fa[u]=ff;adka(ff,u);
        for (i=point[u];i;i=next[i]){
            if ((v=en[i])==ff) continue;
            dfs(v,u);
        }
    }
    int query(int u,int x){
        int head=0,tail,i,v,ans=0;
        que[tail=1]=u;++vt;
        while(head<tail){
            u=que[++head];ans+=(wi[u]>x);
            for (i=point[u];i;i=next[i]){
                if (fa[u]==(v=en[i])) continue;
                if (be[u]!=be[v]){vz[be[v]]=vt;continue;}
                que[++tail]=v;
            }
        }for (head=tail=0,i=1;i<=kz;++i)
            if (vz[i]==vt) que[++tail]=i;
        while(head<tail){
            u=que[++head];v=gete(u,x);
            ans+=cnt[u]-v+1;
            for (i=po[u];i;i=ne[i]) que[++tail]=i;
        }return ans;
    }
    int main(){
        int n,m,i,j,la=0,u,v,op;scanf("%d",&n);
        for (i=1;i<n;++i){scanf("%d%d",&u,&v);add(u,v);}
        for (wi[0]=-1,i=1;i<=n;++i) scanf("%d",&wi[i]);
        scanf("%d",&m);sz=sqrt(n);
        be[0]=anc[0]=kz=1;dfs(1,0);
        for (i=1;i<=kz;++i)
            sort(ka[i]+1,ka[i]+cnt[i]+1,cmp);
        while(m--){
            scanf("%d%d%d",&op,&u,&v);
            u^=la;v^=la;
            if (op==0) printf("%d
    ",la=query(u,v));
            if (op==1) tch(be[u],u,v);
            if (op==2){
                wi[++n]=v;add(u,n);adka(u,n);
                fa[n]=u;ins(be[n],n);
            }
        }
    }
    View Code
  • 相关阅读:
    美团数据治理参考
    Ignite(三): Ignite VS Spark
    Ignite(二): 架构及工具
    Sqlserver 计算两坐标距离函数
    Ignite(一): 概述
    IMDG
    锂电池不一致而产生危害
    平均数_中位数_众数在SqlServer实现
    怎样给孩子取一个好名字?搜狗“有名堂”大数据支招
    eclipse导入外部jar包
  • 原文地址:https://www.cnblogs.com/Rivendell/p/4375683.html
Copyright © 2020-2023  润新知