【bzoj 2038】 [2009国家集训队]小Z的袜子(算法效率--莫队分块算法 模版题)
题意:小Z有N只袜子,有不同的颜色。他有M个提问,问从编号为[L,R]的袜子中随机选一双同色的袜子的概率,用最简分数表示。
解法:经典的莫队算法——无修改、不强制在线(可离线)、状态转移可以一步完成。
步骤如下:
1.对询问按第一关键字的平方根 sqrt(x) 从小到大排序进行分组,再是各组中按第二关键字 y 从小到大排序。O(m log m)。
2.一步步转移,第一关键字最多转移 O(sqrt(n)),第二关键字是 O(n) ,相结合就是 O(n*sqrt(n))。
总时间复杂度就是 O(n*sqrt(n))。
P.S.我第一个代码WA怎么改都对不了,应该是不能直接对颜色的计数数组通过 +1 和 -1 对答案的贡献不同而直接转移状态。而要减去原来该数对答案的贡献,再加上新的贡献。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 typedef long long LL; 9 10 const int N=50010,M=50010,D=50010; 11 int n,m,sqn; 12 int c[N],h[N]; 13 LL ans[M][2]; 14 struct query{int l,r,id;}q[M]; 15 16 bool cmp(query x,query y) 17 { 18 int xx=x.l/sqn,yy=y.l/sqn; 19 if (xx!=yy) return xx<yy; 20 return x.r<y.r; 21 } 22 int read() 23 { 24 char ch=getchar(); 25 int x=0; 26 while (ch<'0'||ch>'9') ch=getchar(); 27 while (ch>='0'&&ch<='9') x=x*10+ch-'0', ch=getchar(); 28 return x; 29 } 30 LL gcd(int x,int y) {return y?gcd(y,x%y):x;} 31 int main() 32 { 33 n=read(),m=read(); 34 for (int i=1;i<=n;i++) c[i]=read(); 35 for (int i=1;i<=m;i++) 36 { 37 q[i].l=read(),q[i].r=read(); 38 q[i].id=i; 39 } 40 sqn=sqrt(n); 41 sort(q+1,q+1+m,cmp); 42 43 int l=1,r=1; 44 LL sum=0; 45 memset(h,0,sizeof(h)); 46 h[c[1]]++; 47 for (int i=1;i<=m;i++) 48 { 49 while (l<q[i].l) sum=sum-2*h[c[l]]+2,h[c[l]]--,l++; 50 while (r>q[i].r) sum=sum-2*h[c[r]]+2,h[c[r]]--,r--; 51 while (l>q[i].l) l--,sum+=2*h[c[l]],h[c[l]]++; 52 while (r<q[i].r) r++,sum+=2*h[c[r]],h[c[r]]++; 53 LL w=(q[i].r-q[i].l)*(q[i].r-q[i].l+1); 54 LL gd=gcd(sum,w); 55 ans[q[i].id][0]=sum/gd,ans[q[i].id][1]=w/gd; 56 } 57 for (int i=1;i<=m;i++) 58 printf("%I64d/%I64d\n",ans[i][0],ans[i][1]); 59 return 0; 60 }
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 using namespace std; 7 const int Max=50010; 8 int n,m,t=0; 9 int s[Max],c[Max],ans1[Max],ans2[Max]; 10 struct hp{int l,r,t,k;} 11 a[Max]; 12 int gcd(int x,int y) 13 { 14 if (x<y) swap(x,y); 15 if (x%y==0) return y; 16 return gcd(y,x%y); 17 } 18 int cmp(const void *x,const void *y) 19 { 20 hp xx=*(hp *)x; 21 hp yy=*(hp *)y; 22 if (xx.t==yy.t) return xx.r-yy.r; 23 return xx.t-yy.t; 24 } 25 int make(int x) 26 { //if (x<2) return 0; 27 if (x%2==0) return (x/2)*(x-1); 28 return x*((x-1)/2); 29 } 30 void block(int id,int l,int r,int ll,int rr) 31 { 32 /*if (ll<l) 33 for (int i=ll;i<l;i++) 34 {//因为ll为0,所以每次都会make(-1),出现负数便错误!//若特殊算第一次,或算1~n为初始值,才行 35 //幸亏我想着算组合make时,若x<2宣布了2个怎么办,没有进一步看x=0或1时不会算错,就加了判断语句,便发现对了一些 36 t-=make(s[c[i]]); 37 s[c[i]]--; 38 t+=make(s[c[i]]); 39 }*/ 40 if (ll<l) 41 for (int i=ll;i<l;i++) 42 { 43 t-=make(s[c[i]]); 44 s[c[i]]--; 45 t+=make(s[c[i]]); 46 } 47 if (ll>l) 48 for (int i=l;i<ll;i++) 49 { 50 t-=make(s[c[i]]); 51 s[c[i]]++; 52 t+=make(s[c[i]]); 53 } 54 if (rr<r) 55 for (int i=rr+1;i<=r;i++) 56 { 57 t-=make(s[c[i]]); 58 s[c[i]]++; 59 t+=make(s[c[i]]); 60 } 61 if (rr>r) 62 for (int i=r+1;i<=rr;i++) 63 { 64 t-=make(s[c[i]]); 65 s[c[i]]--; 66 t+=make(s[c[i]]); 67 } 68 int tt,x; 69 if (!t) {ans1[a[id].k]=0; ans2[a[id].k]=1; return;} 70 tt=make(r-l+1);//t/tt 71 x=gcd(tt,t); 72 ans1[a[id].k]=t/x; 73 ans2[a[id].k]=tt/x; 74 } 75 int main() 76 { 77 //freopen("a.in","r",stdin); 78 //freopen("b.out","w",stdout); 79 scanf("%d%d",&n,&m); 80 81 for (int i=1;i<=n;i++) 82 scanf("%d",&c[i]); 83 double x=sqrt(n); 84 for (int i=1;i<=m;i++) 85 { 86 scanf("%d%d",&a[i].l,&a[i].r); 87 a[i].t=(int)(a[i].l/x); 88 a[i].k=i; 89 } 90 qsort(a+1,m,sizeof(hp),cmp); 91 92 memset(s,0,sizeof(s)); 93 //a[0].l=0; a[0].r=0; 94 a[0].l=1;a[0].r=n; 95 for (int i=1;i<=n;i++) s[c[i]]++; 96 for (int i=1;i<=n;i++) 97 t+=make(s[i]); 98 for (int i=1;i<=m;i++) 99 block(i,a[i].l,a[i].r,a[i-1].l,a[i-1].r); 100 for (int i=1;i<=m;i++) 101 printf("%d/%d\n",ans1[i],ans2[i]); 102 //system("pause"); 103 return 0; 104 }