zoukankan      html  css  js  c++  java
  • 【莫队】专题总结

    一直要写莫队的总结都没时间写- -

    今天下午把它写掉吧

    莫队的思路就是对询问分块然后排序 使得总时间复杂度优化一个√(n)级别

    具体还是拿一道题目来说吧- -

    例题

    【2009国家集训队】 小z的袜子

    给出n个数表示n只袜子的颜色 询问l、r 表示区间[l,r] 内随机抽取两只袜子同色的概率

    显然就是要求区间[l,r] 内有几对袜子是颜色相同的

    暴力的做法 维护num[i]表示颜色为i的袜子有几只 时间O(nm)

    莫队的做法就是 先把n只袜子分成√n块 每块√n

    把所有的询问排序 按第一关键字为l所属的块 第二关键字为r

    把第一关键字相同的一起处理

    每块只有√n个点 所以相邻两次询问的l最多差√n 而r递增 所以这些询问的r变化总和为O(n)的

    总时间复杂度O(n√n)

    代码

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 typedef long long ll;
     6 using namespace std;
     7 const ll N=50001;
     8 struct info{
     9     ll x,y,t;
    10     info(const ll a=0,const ll b=0,const ll c=0):
    11         x(a),y(b),t(c){}
    12 }ask[N],save[N];
    13 ll n,m,add,ns,col[N],num[N],ans[N],anss;
    14 inline bool cmp(info a,info b){ return a.y<b.y; }
    15 ll gcd(ll x,ll y){ return y ? gcd(y,x%y) : x; }
    16 void addnum(ll x,ll y,ll s){
    17     for (ll i=x;i<=y;i++){
    18         if (num[col[i]]) anss-=num[col[i]]*(num[col[i]]-1);
    19         num[col[i]]+=s;
    20         anss+=num[col[i]]*(num[col[i]]-1);
    21     }
    22 }
    23 void work(){
    24     ll add=(int)sqrt(n);
    25     for (ll i=1;i<=n;i+=add){
    26         memset(num,0,sizeof(num));
    27         anss=0;
    28         ns=0;
    29         for (ll j=1;j<=m;j++)
    30         if (i<=ask[j].x && ask[j].x<i+add) save[++ns]=ask[j];
    31         sort(save+1,save+ns+1,cmp);
    32         for (ll last=i+add,j=1;j<=ns;j++){
    33             if (save[j].y<i+add) addnum(save[j].x,save[j].y,1);
    34             else{
    35                 addnum(save[j].x,i+add-1,1);
    36                 addnum(last,save[j].y,1);
    37                 last=save[j].y+1;
    38             }
    39             ans[save[j].t]=anss;
    40             if (save[j].y<i+add) addnum(save[j].x,save[j].y,-1);
    41             else  addnum(save[j].x,i+add-1,-1);
    42         }
    43     }
    44 }
    45 int main(){
    46     freopen("bz2038.in","r",stdin);
    47     freopen("bz2038.out","w",stdout);
    48     scanf("%I64d%I64d",&n,&m);
    49     for (ll i=1;i<=n;i++) scanf("%I64d",&col[i]);
    50     for (ll x,y,i=1;i<=m;i++){
    51         scanf("%I64d%I64d",&x,&y);
    52         ask[i]=info(x,y,i);
    53     }
    54     work();
    55     for (ll i=1;i<=m;i++){
    56         ll x=ans[i],y=ask[i].y-ask[i].x+1;
    57         if (y==1){
    58             puts("0/1");
    59             continue;
    60         }
    61         y=y*(y-1);
    62         ll gc=gcd(x,y);
    63         printf("%I64d/%I64d
    ",x/gc,y/gc);
    64     }
    65     fclose(stdin);
    66     fclose(stdout);
    67 }
    View Code

    带询问

    但是这题没有修改操作 如果有修改操作怎么办呢- -

    我们可以把n平均分为3√n块

    排序时第一关键字为l所在的块 第二关键字为r所在的块 第三关键字为询问/操作时间ti

    对于前两个关键字相同的一起处理 处理这么一堆的时间复杂度为O(n) 所以总时间复杂度为O(n^(5/3))

    支持在线

    还有一种比较快的方法 而且支持在线(其实感觉都不叫莫队了 明明就是分块)

    维护f[x][y]表示第x块到第y块的答案 每次修改O((3√n)^2) 询问则只要O(n/(3√n))

    但是有个缺点 有时候需要维护的值不止一个 如上面每种袜子的个数 空间很可能会不够用

  • 相关阅读:
    天梯赛 社交集群(并查集)
    蓝桥杯 正则问题(dfs)
    天梯赛L3-001. 凑零钱(01背包记录物品)
    天梯赛/PAT 二叉树总结
    GPLT天梯赛 L2-022. 重排链表
    蓝桥杯 2的次幂表示(递归)
    排列与组合的一些定理
    卡特兰数
    洛谷P1349 广义斐波那契数列(矩阵快速幂)
    Manacher's Algorithm 马拉车算法(最长回文串)
  • 原文地址:https://www.cnblogs.com/g-word/p/3757258.html
Copyright © 2011-2022 走看看