[国家集训队]数颜色 / 维护队列

毒瘤莫队卡常题,卡了一早上的常数,才开O2勉强过。

带修莫队模板题。

普通莫队要离线下来做,遇到这种带修改的题目直接就萎了,但是全国广大的OIer们在莫队的基础上,发明了带修莫队这种玄学算法。

具体来说就是给修改和求值打上时间戳,然后在普通莫队双指针的基础上增加一个指针tim指向时间,在这一个维度上面跳来跳去,直到跳到左指针指向左端点,右指针指向右端点,tim指针指向当前求值的时间戳为止。

排序依然是按左端点所在块排序,同一块内的按右端点的块排序,否则按时间排序。

值得一提的是,在修改的时候,由于我们的tim指针可能“往回跳”,所以我们要记下来之前的值,因此我们可以直接swap这俩值,回来的时候就直接swap回来。

最后一点就是块的大小,如果我们还像普通莫队那样,把块的大小直接设成$\sqrt{n}$,我们的时间复杂度会被卡成$O(n^2)$,因此有人证明了块大小为$\sqrt[3]{n^4t}$时最优(t为修改次数),但是有一个更简单的设定大小方式,即把块的大小设为 $n^{\frac{2}{3}}$ ,这样的复杂度为 $O(n^{\frac{5}{3}})$。

 1 //#pragma GCC optimize(3)
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<cmath>
 7 #define N 1333340
 8 using namespace std;
 9 int read()
10 {
11     int x=0,f=1;char ch=getchar();
12     while(ch<'0'||ch>'9')ch=getchar();
13     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
14     return x*f;
15 }
16 struct query
17 {
18     int l,r,tim,x;
19 }q[N];
20 struct modify
21 {
22     int pos,color;
23 }c[N];
24 int n,m,cntq,cntc,l=1,r,tim,now,a[N],belong[N],siz,bnum,cnt[N],ans[N];
25 char opt;
26 bool cmp(query a,query b)
27 {
28     return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.r]^belong[b.r])?belong[a.r]<belong[b.r]:(a.tim<b.tim));
29 }
30 int main()
31 {
32     n=read();m=read();
33     siz=pow(n,2.0/3.0);
34     bnum=ceil((double)(n/siz));
35     for(int i=1;i<=bnum;i++)
36     {
37         for(int j=(i-1)*siz+1;j<=i*siz;j++)
38         {
39             belong[j]=i;
40         }
41     }
42     for(int i=1;i<=n;i++)a[i]=read(); 
43     for(int i=1;i<=m;i++)
44     {
45         opt=getchar();getchar();
46         if(opt=='Q')
47         {
48             q[++cntq].l=read();
49             q[cntq].r=read();
50             q[cntq].tim=cntc;
51             q[cntq].x=cntq; 
52         }
53         else 
54         {
55             c[++cntc].pos=read();
56             c[cntc].color=read();
57         }
58     }
59     sort(q+1,q+cntq+1,cmp);
60     for(int i=1;i<=cntq;i++)
61     {
62         int ll=q[i].l,rr=q[i].r,tt=q[i].tim;
63         while(l<ll)now-=!--cnt[a[l++]];
64         while(l>ll)now+=!cnt[a[--l]]++;
65         while(r<rr)now+=!cnt[a[++r]]++;
66         while(r>rr)now-=!--cnt[a[r--]];
67         while(tim<tt)
68         {
69             ++tim;
70             if(c[tim].pos>=ll&&c[tim].pos<=rr)now-=!--cnt[a[c[tim].pos]]-!cnt[c[tim].color]++;
71             swap(a[c[tim].pos],c[tim].color);        
72         }
73         while(tim>tt)
74         {
75             if(c[tim].pos>=ll&&c[tim].pos<=rr)now-=!--cnt[a[c[tim].pos]]-!cnt[c[tim].color]++;
76             swap(a[c[tim].pos],c[tim].color);
77             --tim;
78         }
79         ans[q[i].x]=now;
80     }
81     for(int i=1;i<=cntq;i++)printf("%d\n",ans[i]);
82     return 0;
83 }
View Code

 

转载于:https://www.cnblogs.com/szmssf/p/11547103.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值