分块 莫队
学习了一下很基本的分块和莫队算法,因为不太会写曼哈顿距离最小生成树,所以就写了个分块版本的(分四种情况,大概这个意思吧)。。。
分块
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()); } }
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); } } } }
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); } }
莫队
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); }
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); }
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); }
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]); }
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]); }
这题还有一种做法:同平衡二叉树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]); }
树分块
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); } } }