分块 分块+莫队算法
我还是感觉我的分块怪怪的,我把左端点按所在块排序,相同时按右端点排序(均为升序),然后从上一个区间的答案转移到这个区间的答案。
A过之后,我把左端点按所在块排序变为直接按左端点排序,其余不变,结果T了。。。。。。
哪位神犇能告诉我为什么是这样的,抑或我写的是不是分块,蒟蒻不胜感激
PS:经过一番搜题解后,我大概了解自己写的是啥了。这题的询问[L,R]可以在O(1)时间内转移到[L+1,R]、[L-1,R]、[L,R-1]、[L,R+1]那么我们可以采用莫队算法,那么从[L,R]转移到[L',R']的代价就是O(|L-L'|+|R-R'|),我们需要合理的安排处理询问的次序,最严格的方法是把每个询问看作二维平面中一个点(L,R),求出这m个询问的曼哈顿距离最小生成树,可以证明复杂度上限为O(NsqrtN)。但貌似可以用分块来替代MST,方法就是把左端点按所在块排序,相同时按右端点排序,这样一个块内的查询是O(N)的,总复杂度也是O(NsqrtN)
code:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int a[50001],b[50001],num[50001]; long long ans[50001][2]; long long sum; int n,m,L,R; struct hp{ int l,r,st,num; }qst[50001]; int cmp(const hp &a,const hp &b) { if ((a.st<b.st)||(a.st==b.st&&a.r<b.r)) return 1; else return 0; } long long gcd(long long a,long long b) { if (a%b==0) return b; else return gcd(b,a%b); } long long C(int m,int n) { long long now=1; if (m<n-m) m=n-m; int i=m+1,j=1; while (i<=n&&j<=n-m) {now=now*i/j; i++; j++;} if (i<n) { while (i<=n) {now=now*i; i++;} } return now; } void work(int i) { int l,r; l=qst[i].l; r=qst[i].r; while (l>L) { num[a[L]]--; if (num[a[L]]==1) sum=sum-C(2,num[a[L]]+1); if (num[a[L]]>1) sum=sum-C(2,num[a[L]]+1)+C(2,num[a[L]]); L++; } while (l<L) { L--; num[a[L]]++; if (num[a[L]]==2) sum=sum+C(2,num[a[L]]); if (num[a[L]]>2) sum=sum-C(2,num[a[L]]-1)+C(2,num[a[L]]); } while (r>R) { R++; num[a[R]]++; if (num[a[R]]==2) sum=sum+C(2,num[a[R]]); if (num[a[R]]>2) sum=sum-C(2,num[a[R]]-1)+C(2,num[a[R]]); } while (r<R) { num[a[R]]--; if (num[a[R]]==1) sum=sum-C(2,num[a[R]]+1); if (num[a[R]]>1) sum=sum-C(2,num[a[R]]+1)+C(2,num[a[R]]); R--; } ans[qst[i].num][0]=sum; } int main() { int i,size,per; long long t; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); size=unique(b+1,b+n+1)-b-1; for (i=1;i<=n;++i) a[i]=upper_bound(b+1,b+size+1,a[i])-b-1; per=sqrt(n); for (i=1;i<=m;++i) { scanf("%d%d",&qst[i].l,&qst[i].r); qst[i].st=qst[i].l/per+1; qst[i].num=i; } sort(qst,qst+m+1,cmp); ans[qst[1].num][1]=C(2,qst[1].r-qst[1].l+1); for (i=qst[1].l;i<=qst[1].r;++i) { num[a[i]]++; if (num[a[i]]==2) sum+=C(2,num[a[i]]); if (num[a[i]]>2) sum=sum-C(2,num[a[i]]-1)+C(2,num[a[i]]); } ans[qst[1].num][0]=sum; L=qst[1].l; R=qst[1].r; for (i=2;i<=m;++i) { ans[qst[i].num][1]=C(2,qst[i].r-qst[i].l+1); work(i); } for (i=1;i<=m;++i) { t=gcd(ans[i][0],ans[i][1]); ans[i][0]/=t; ans[i][1]/=t; printf("%lld/%lld\n",ans[i][0],ans[i][1]); } }