• 莫队


    给定长度为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
    ");
    63             else printf("%I64d/%I64d
    ",e[i].ans/d,x/d);
    64         }
    65     }
    66 }
    67 int main(){
    68     OI::work();
    69     return 0;
    70 }
    View Code

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

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

  • 相关阅读:
    Python中return self的用法
    多分类问题的交叉熵计算
    Python爬虫之足球小将动漫(图片)下载
    Sklearn中二分类问题的交叉熵计算
    TensorFlow.js入门(一)一维向量的学习
    MySql 流程控制经典案列讲解
    MySql 流程控制
    MySql 函数
    MySql 存储过程
    MySql 视图
  • 原文地址:https://www.cnblogs.com/chadinblog/p/5898011.html
Copyright © 2020-2023  润新知