分块 莫队

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

分块

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\n",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\n",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\n",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\n",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\n",ans[i].zi/sum,ans[i].mu/sum);
        }
        else printf("0/1\n");
    }
    
    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\n",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\n",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\n",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\n",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\n",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\n",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

 

posted @ 2015-03-29 14:35  Rivendell  阅读(477)  评论(0编辑  收藏  举报