莫队
给定长度为n的数列以及正整数,有m个询问每次询问给定两个数l,r,求l≤i≤j≤r,且a(i) xor a(i+1) xor a(i+2) xor ... xor a(j)=k的(i,j)的个数。
这道题是莫队入门,但我认为[小z的袜子]那道题比这道题不知道简单到哪里去了。
莫队做法,按询问的左端点排序,将询问分块,每个块内按右端点排序,然后每个块内顺序求就可以了;
时间复杂度证明网上有很多,O(n^(3/2));
主要需要吐槽的是代码尽管不长,但很令人恶心;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<ctime> 7 #include<vector> 8 #include<algorithm> 9 #include<queue> 10 #include<map> 11 #include<cmath> 12 using namespace std; 13 #define LL long long 14 #define FILE "1" 15 #define up(i,j,n) for(int i=j;i<=n;i++) 16 #define down(i,n,j) for(int i=n;i>=j;i--) 17 namespace OI{ 18 const int maxn=100010; 19 int n,m,k,p,a[maxn],f[maxn],cnt[maxn*100]; 20 struct node{ 21 LL x,y,ans,id; 22 bool operator<(const node& b)const{return x<b.x;} 23 }e[maxn],c[maxn]; 24 bool mycmp(node b,node c){return b.y<c.y;} 25 bool mp(node b,node c){return b.id<c.id;} 26 void init(){ 27 scanf("%d%d%d",&n,&m,&k); 28 up(i,1,n)scanf("%d",&a[i]); 29 up(i,1,n)a[i]^=a[i-1]; 30 up(i,1,m){scanf("%d%d",&e[i].x,&e[i].y);e[i].id=i;} 31 sort(e+1,e+m+1); 32 } 33 void work(){ 34 p=(int)sqrt(m*1.0); 35 for(int i=1;i<=m/p;i++){ 36 memset(cnt,0,sizeof(cnt)); 37 sort(e+(i-1)*p+1,e+i*p+1,mycmp); 38 long long u=0,v=0,ans=0;cnt[0]++; 39 for(int j=(i-1)*p+1;j<=i*p;j++){ 40 while(v<e[j].y)v++,ans+=cnt[a[v]^k],cnt[a[v]]++; 41 while(u<e[j].x-1){cnt[a[u]]--;ans-=cnt[a[u]^k];u++;} 42 while(u>e[j].x-1){u--;ans+=cnt[a[u]^k];cnt[a[u]]++;} 43 e[j].ans=ans; 44 } 45 } 46 if(m%p){ 47 memset(cnt,0,sizeof(cnt)); 48 sort(e+(m/p)*p+1,e+m+1,mycmp); 49 long long u=0,v=0,ans=0;cnt[0]++; 50 for(int j=(m/p)*p+1;j<=m;j++){ 51 while(v<e[j].y)v++,ans+=cnt[a[v]^k],cnt[a[v]]++; 52 while(u<e[j].x-1){cnt[a[u]]--;ans-=cnt[a[u]^k];u++;} 53 while(u>e[j].x-1){u--;ans+=cnt[a[u]^k];cnt[a[u]]++;} 54 e[j].ans=ans; 55 } 56 } 57 sort(e+1,e+m+1,mp); 58 for(int i=1;i<=m;i++)cout<<e[i].ans<<endl; 59 } 60 61 } 62 int main(){ 63 using namespace OI; 64 init(); 65 work(); 66 cout<<clock()<<endl; 67 }
值得欣慰的是在写完这道题后对莫队有了全新的认识,我相信以后就算再恶心的代码我也会写了;
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。
这道题和上一道题比起来太简单了些;
同样是莫队,上一题好歹还需要考虑前缀xor的影响,这一题直接就是最简单的那种;
不说了,上代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<ctime> 7 #include<vector> 8 #include<algorithm> 9 #include<queue> 10 #include<map> 11 #include<cmath> 12 using namespace std; 13 #define LL long long 14 #define FILE "1" 15 #define up(i,j,n) for(int i=j;i<=n;i++) 16 #define down(i,n,j) for(int i=n;i>=j;i--) 17 namespace OI{ 18 const int maxn=50500; 19 int n,m,a[maxn],c[maxn],p; 20 struct node{ 21 int x,y,id; 22 LL ans; 23 }e[maxn]; 24 bool mycmp(node a,node b){return a.x<b.x;} 25 bool mycmd(node a,node b){return a.y<b.y;} 26 bool myid(node a,node b){return a.id<b.id;} 27 LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);} 28 void init(){ 29 scanf("%d%d",&n,&m); 30 up(i,1,n)scanf("%d",&a[i]); 31 up(i,1,m){scanf("%d%d",&e[i].x,&e[i].y);e[i].id=i;} 32 sort(e+1,e+m+1,mycmp); 33 } 34 void slove(int left,int right){ 35 sort(e+left,e+right+1,mycmd); 36 memset(c,0,sizeof(c)); 37 int u=0,v=0;c[0]=1;LL ans=0; 38 for(int j=left;j<=right;j++){ 39 while(v<e[j].y){v++;ans+=c[a[v]];c[a[v]]++;} 40 while(u<e[j].x){c[a[u]]--;ans-=c[a[u]];u++;} 41 while(u>e[j].x){u--;ans+=c[a[u]];c[a[u]]++;} 42 e[j].ans=ans; 43 } 44 } 45 void work(){ 46 init(); 47 p=(int)sqrt(m*1.0); 48 int left=0,right=0; 49 for(int i=1;i<=m/p;i++){ 50 left=(i-1)*p+1,right=i*p; 51 slove(left,right); 52 } 53 if(m%p){ 54 left=m/p*p+1,right=m; 55 slove(left,right); 56 } 57 sort(e+1,e+m+1,myid); 58 LL d,x; 59 up(i,1,m){ 60 x=((LL)e[i].y-e[i].x+1)*((LL)e[i].y-e[i].x)/2; 61 d=gcd(x,e[i].ans); 62 if(!e[i].ans)printf("0/1\n"); 63 else printf("%I64d/%I64d\n",e[i].ans/d,x/d); 64 } 65 } 66 } 67 int main(){ 68 OI::work(); 69 return 0; 70 }
我发现了网上的代码版本有两种,一种是按照询问分块,一种是按照原数组分块,这两种都可以,但是按照询问分块一般都比按数组分块的快,而且比较好写;
实测这个代码交上去要300ms,改成按数组分块的要2s多一些;