莫队

给定长度为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 }
View Code

值得欣慰的是在写完这道题后对莫队有了全新的认识,我相信以后就算再恶心的代码我也会写了;

作为一个生活散漫的人,小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 }
View Code

我发现了网上的代码版本有两种,一种是按照询问分块,一种是按照原数组分块,这两种都可以,但是按照询问分块一般都比按数组分块的快,而且比较好写;

实测这个代码交上去要300ms,改成按数组分块的要2s多一些;

posted @ 2016-09-22 21:20  CHADLZX  阅读(353)  评论(0编辑  收藏  举报