zoukankan      html  css  js  c++  java
  • 莫队算法学习笔记

    从我的$Luogu$博客上搬运过来的$QwQ$,很久以前写的了,趁着这次考试考到了复习一下


    莫队分为三种:普通莫队,树形莫队,带修改莫队。

    例 1】小Z的袜子 题目传送门

    题目大意:进行区间询问$[l,r]$,输出在该区间内随机抽两次抽到相同颜色袜子的概率。

    分析:首先考虑对于一个长度为$n$的区间内的答案如何求解。题目要求答案用最简分数表示:那么分母就是$n*(n-1)($表示两两袜子之间的随机组合$)$,分子是将该区间内每种颜色$i$出现次数的平方的累加求出的和。

    莫队思路:离线情况下对所有的询问进行一个$sort$排序,然后两个指针$l,r$不断以“暴力”的方式在区间内跳来跳去$($所以到底应该怎么跳嘞?这不是相当于没讲吗? $)$,最终输出答案。

    思想基础:$($关于如何“跳来跳去”$)$

    $<1>$两个询问之间的状态跳转。假设当前完成询问的区间为$[a,b]$,下一个询问的区间为$[p,q]$,现在保存$[a,b]$区间内的每个颜色出现次数$sum$和答案$ans$,通过移动指针来转移答案。

    $<2>$考虑指针向左或向右移动一个单位,我们要付出多大的代价才能维护$sum$和$ans($即让$sum$和$ans$保存的是当前$[l,r]$的正确信息,我终于知道维护的真实含义了$)$。

    $<3>$假设指针移动一个单位后多出来$(or$减少$)$的袜子颜色为$a$,那么就更改$sum[a](++or- -)$,同时维护$ans$的值,分母由$n^2$变为$(n-1)^2 or (n+1)^2$,分子要减去原来答案的贡献$($即原$sum[a]^2)$,再加上现在的答案贡献$($即现$sum[a]^2)$。

    优化:由于以上方法最坏情况时间复杂度为$O(n^2)$,所以要用排序优化一下。

    分块用$pos$数组存储每个$l$在哪一块。对询问进行排序,如果$pos[l]$相同,那么就以$r$为关键字排序;否则以$l$为关键字排序。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=50003;
     4 struct Modui{//开一个结构体存储每个询问
     5     int l,r,id;
     6     long long A,B;
     7 }q[N];
     8 long long gcd(long long a,long long b){//求最大公约数,用于约分
     9     long long mid=b;//蒟蒻的我只会用辗转相除法QAQ
    10     b=a%b;
    11     if(b==0) return mid;
    12     else return gcd(mid,b);
    13 }
    14 int n,m,color[N],t,pos[N];
    15 long long sum[N],ans;//ans存储当前区间抽中两只同色袜子的方案总数
    16 bool cmp1(Modui a,Modui b){
    17     return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;//排序,判断关键字
    18 }
    19 bool cmp2(Modui a,Modui b){//输出前按输入的顺序排序
    20     return a.id<b.id;
    21 }
    22 void change(int x,int add){//转移答案
    23     ans-=sum[color[x]]*(sum[color[x]]-1);
    24     sum[color[x]]+=add;
    25     ans+=sum[color[x]]*(sum[color[x]]-1);
    26     return;
    27 }
    28 int main(){
    29     scanf("%d%d",&n,&m);
    30     t=sqrt(n);//t存储分块的数量
    31     for(int i=1;i<=n;i++)
    32         scanf("%d",&color[i]),pos[i]=i/t+1;
    33     for(int i=1;i<=m;i++)
    34         scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    35     sort(q+1,q+m+1,cmp1);
    36     int l=1,r=0;//l,r两个指针存储当前的位置
    37     for(int i=1;i<=m;i++){
    38         while(l<q[i].l) change(l,-1),l++;
    39         while(l>q[i].l) change(l-1,1),l--;
    40         while(r<q[i].r) change(r+1,1),r++;
    41         while(r>q[i].r) change(r,-1),r--;//把指针移动到当前询问的区间
    42         if(q[i].l==q[i].r) {q[i].A=0;q[i].B=1;continue;}
    43         //只有一只袜子的情况要特判
    44         q[i].A=ans;//A代表分子,等于方案总数
    45         q[i].B=1LL*(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
    46         //B代表分母,等于区间内所有袜子组合的方案总数
    47         long long GCD=gcd(q[i].A,q[i].B);
    48         q[i].A/=GCD;q[i].B/=GCD;//约分
    49     }
    50     sort(q+1,q+m+1,cmp2);
    51     for(int i=1;i<=m;i++)//排序后按序输出
    52         printf("%lld/%lld
    ",q[i].A,q[i].B);
    53     return 0;
    54 }
    代码戳这里
  • 相关阅读:
    Vim+Vundle+YouCompleteMe 安装
    TortoiseSVN 的分支合并操作
    JSP数据交互一
    Jquery操作DOM
    Jquery
    JQuery选择器
    第五章初始JQuery
    JavaScript对象及初识面向对象
    JavaScript第三章操作DOM
    JavaScript第二章操作BOM
  • 原文地址:https://www.cnblogs.com/THWZF/p/11708726.html
Copyright © 2011-2022 走看看