【莫队】专题总结

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

今天下午把它写掉吧

 

莫队的思路就是对询问分块然后排序 使得总时间复杂度优化一个√(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\n",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))

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

转载于:https://www.cnblogs.com/g-word/p/3757258.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值