RememberMePlease 2020-06-26
gym卡了莫队,于是趁这个机会学一下莫队
莫队的核心是分块排序,这种特殊的排序方法将任务按排序后的顺序完成,可以在解决绝大多数无修改的离线区间问题中极大的优化时间(优化了$\sqrt n$左右)。
题意:n个数,寻问10000次,任意区间内的相等数的次数的立方和。
题解:将n个数离散化后,用莫队将询问分块排序,暴力计算出答案,在按原顺序输出结果。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long using namespace std; ll n,a[100007],t,head,maze,size,ak[100007],num[100007],ans[100007],b[100007]; struct madoka{ ll l; ll r; ll p; }ma[100007]; long long int qpow(long long int x,long long int n) { long long int res=1; while(n>0) { if(n%2==1) { res=res*x; res=res; } x=x*x; x=x; n>>=1; } return res; } bool cmp(madoka a1,madoka a2){ if(ak[a1.l]==ak[a2.l]){ return a1.r<a2.r; } return ak[a1.l]<ak[a2.l]; } void go(){ ll l=1,r=1,ansn=1; num[a[1]]++; for(int i=1;i<=t;i++){ while(r<ma[i].r){ r++; ansn-=qpow(num[a[r]],3); num[a[r]]++; ansn+=qpow(num[a[r]],3); } while(l>ma[i].l){ l--; ansn-=qpow(num[a[l]],3); num[a[l]]++; ansn+=qpow(num[a[l]],3); } while(r>ma[i].r){ ansn-=qpow(num[a[r]],3); num[a[r]]--; ansn+=qpow(num[a[r]],3); r--; } while(l<ma[i].l){ ansn-=qpow(num[a[l]],3); num[a[l]]--; ansn+=qpow(num[a[l]],3); l++; } ans[ma[i].p]=ansn; } } int main(){ while(scanf("%lld",&n)!=EOF){ memset(num,0,sizeof(num)); size=(ll)sqrt(n*1.0); maze=ceil((double)n/size); head=1; for(int i=1;i<=maze;i++){ for(int j=head;j<head+size&&j<=n;j++){ ak[j]=i; } head+=size; } int m=0; for(int i=1;i<=n;++i) { scanf("%lld",&a[i]); b[++m]=a[i]; } sort(b+1,b+1+m); m=unique(b+1,b+1+m)-b-1; for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+m,a[i])-b; scanf("%lld",&t); for(int i=1;i<=t;i++){ scanf("%lld%lld",&ma[i].l,&ma[i].r); ma[i].p=i; } sort(ma+1,ma+1+t,cmp); go(); for(int i=1;i<=t;i++){ printf("%lld\n",ans[i]); } } //return 0; }
题意:n个数,寻问10000次,任意区间内的相等数的次数与其数相等的个数。
题解:与上题差不多,但有个坑要注意下,离散化后会导致值改变,所以某数的次数要与它离散化前比较,之后便是莫队板子。
#include<iostream> #include<algorithm> #include<cmath> using namespace std; int n,m,t,a[100007],b[100007],num[100007],size,maze,head,ak[100007],ans[100007]; struct madoka{ int l; int r; int p; }ma[100007]; bool cmp(madoka a1,madoka a2){ if(ak[a1.l]==ak[a2.l]){ return a1.r<a2.r; } return ak[a1.l]<ak[a2.l]; } void go(){ int l=2,r=1,ansn=0; for(int i=1;i<=t;i++){ while(r<ma[i].r){ r++; if(num[a[r]]==b[a[r]]){ ansn--; } num[a[r]]++; if(num[a[r]]==b[a[r]]){ ansn++; } } while(r>ma[i].r){ if(num[a[r]]==b[a[r]]){ ansn--; } num[a[r]]--; if(num[a[r]]==b[a[r]]){ ansn++; } r--; } while(l>ma[i].l){ l--; if(num[a[l]]==b[a[l]]){ ansn--; } num[a[l]]++; if(num[a[l]]==b[a[l]]){ ansn++; } } while(l<ma[i].l){ if(num[a[l]]==b[a[l]]){ ansn--; } num[a[l]]--; if(num[a[l]]==b[a[l]]){ ansn++; } l++; } ans[ma[i].p]=ansn; } } int main(){ scanf("%d%d",&n,&t); size=sqrt(n); maze=ceil((double)n/size); head=1; for(int i=1;i<=maze;i++){ for(int j=head;j<head+size&&j<=n;j++){ ak[j]=i; } head+=size; } int m=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[++m]=a[i]; } sort(b+1,b+1+m); m=unique(b+1,b+1+m)-b-1; for(int i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+1+m,a[i])-b; } for(int i=1;i<=t;i++){ scanf("%d%d",&ma[i].l,&ma[i].r); ma[i].p=i; } sort(ma+1,ma+1+t,cmp); go(); for(int i=1;i<=t;i++){ printf("%d\n",ans[i]); } }