[模板] 莫队/树上莫队/带修莫队

莫队

如果知道[l,r]的答案时能快速求出[l+1,r][l,r+1][l-1,r][l,r-1]的答案,那么可以用莫队离线求解

如果要从[l,r]得到[l',r'],那么需要$O(|r'-r|+|l'-l|)$次更新答案

所以需要确定一个求答案的顺序使得这玩意最优

以$\frac{n}{q}$分块,按l所在块为第一关键字,r为第二关键字排序

总复杂度是$O(n \sqrt q)$

luogu1494 小Z的袜子

询问$\sum{cnt[i]*(cnt[i]-1)}$,其中cnt[i]是每种颜色出现的次数

可以发现当$cnt[i]+=d$时,这玩意会$+=d^2+2*cnt[i]*d-d$

于是记每种颜色出现的次数就好了

 1 #include<bits/stdc++.h>
 2 #define CLR(a,x) memset(a,x,sizeof(a))
 3 using namespace std;
 4 typedef long long ll;
 5 typedef unsigned long long ull;
 6 typedef pair<int,int> pa;
 7 const int maxn=5e4+10;
 8 
 9 inline ll rd(){
10     ll x=0;char c=getchar();int neg=1;
11     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
12     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
13     return x*neg;
14 }
15 
16 int N,M,col[maxn],cnt[maxn],sqn;
17 ll ans[maxn],len[maxn];
18 struct Node{
19     int l,r,i;
20 }q[maxn];
21 
22 inline bool cmp(Node a,Node b){
23     return a.l/sqn==b.l/sqn?a.r<b.r:a.l<b.l;
24 }
25 
26 inline ll gcd(ll x,ll y){
27     if(!x) return y;
28     return gcd(y%x,x);
29 }
30 
31 int main(){
32     //freopen("","r",stdin);
33     int i,j,k;
34     N=rd(),M=rd();sqn=sqrt(N);
35     for(i=1;i<=N;i++) col[i]=rd();
36     for(i=1;i<=M;i++){
37         q[i].l=rd(),q[i].r=rd(),q[i].i=i;
38         len[i]=1ll*(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
39     }
40     sort(q+1,q+M+1,cmp);
41     int l=1,r=0;
42     ll tmp=0;
43     for(i=1;i<=M;i++){
44         if(q[i].l==q[i].r) len[q[i].i]=1;
45         while(r<q[i].r){
46             tmp+=cnt[col[++r]]*2;
47             cnt[col[r]]++;
48         }
49         while(r>q[i].r){
50             tmp+=2-cnt[col[r]]*2;
51             cnt[col[r--]]--;
52         }
53         while(l>q[i].l){
54             tmp+=cnt[col[--l]]*2;
55             cnt[col[l]]++;
56         }
57         while(l<q[i].l){
58             tmp+=2-cnt[col[l]]*2;
59             cnt[col[l++]]--;
60         }
61         ans[q[i].i]=tmp/gcd(tmp,len[q[i].i]),len[q[i].i]/=gcd(tmp,len[q[i].i]);
62     }
63     for(i=1;i<=M;i++){
64         
65         printf("%lld/%lld\n",ans[i],len[i]);
66     }
67     return 0;
68 }
View Code

 

luogu3709 大爷的字符串题

描述比较迷,但是分析以后给一个构造就是每次那个区间最少能给他排成几个严格递增的序列,不难发现就是众数的个数

于是莫队,记$cnt[i]$是$i$出现的次数,$num[j]$是$cnt$为$j$出现的次数,以及目前的最大$cnt$,我这个最大的$cnt$的数量被减没了,就换到原来的最大的$-1$

 1 #include<bits/stdc++.h>
 2 #define CLR(a,x) memset(a,x,sizeof(a))
 3 #define MP make_pair
 4 using namespace std;
 5 typedef long long ll;
 6 typedef unsigned long long ull;
 7 typedef pair<int,int> pa;
 8 const int maxn=2e5+10;
 9 
10 inline ll rd(){
11     ll x=0;char c=getchar();int neg=1;
12     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
13     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
14     return x*neg;
15 }
16 
17 int N,NN,M,a[maxn],tmp[maxn];
18 int ans[maxn],cnt[maxn],num[maxn];
19 struct Node{
20     int l,r,i;
21 }q[maxn];
22 
23 inline bool cmp(Node x,Node y){
24     return x.l/NN==y.l/NN?x.r<y.r:x.l<y.l;
25 }
26 
27 int main(){
28     //freopen("","r",stdin);
29     int i,j,k;
30     N=rd(),M=rd();NN=sqrt(N);
31     for(i=1;i<=N;i++){
32         a[i]=tmp[i]=rd();
33     }sort(tmp+1,tmp+N+1);
34     j=unique(tmp+1,tmp+N+1)-tmp;
35     for(i=1;i<=N;i++)
36         a[i]=lower_bound(tmp+1,tmp+j,a[i])-tmp;
37     for(i=1;i<=M;i++){
38         q[i].l=rd(),q[i].r=rd(),q[i].i=i;
39         ans[i]=0;
40     }
41     sort(q+1,q+M+1,cmp);
42     
43     int ma=0,l=1,r=0;
44     for(i=1;i<=M;i++){
45         while(r<q[i].r){
46             cnt[a[++r]]++;
47             num[cnt[a[r]]]++,num[cnt[a[r]]-1]--;
48             if(cnt[a[r]]>ma) ma=cnt[a[r]];
49         }while(r>q[i].r){
50             cnt[a[r]]--;
51             num[cnt[a[r]]]++,num[cnt[a[r]]+1]--;
52             if(!num[ma]) ma--;
53             r--;
54         }
55         while(l>q[i].l){
56             cnt[a[--l]]++;
57             num[cnt[a[l]]]++,num[cnt[a[l]]-1]--;
58             if(cnt[a[l]]>ma) ma=cnt[a[l]];
59         }while(l<q[i].l){
60             cnt[a[l]]--;
61             num[cnt[a[l]]]++,num[cnt[a[l]]+1]--;
62             if(!num[ma]) ma--;
63             l++;
64         }
65         ans[q[i].i]-=ma;
66     }
67     for(i=1;i<=M;i++)
68         printf("%d\n",ans[i]);
69     return 0;
70 }
View Code

 

树上莫队

针对树上路径的询问

欧拉序做法

考虑一棵树的欧拉序(即括号序,进和出时各记一次),如果对于一个区间,某个元素出现了两次,则认为它没出现过,那么对于路径(a,b)有两种情况(不妨设a在b前面):

1.$a$或$b$中有一个是$lca$:$[in[a],in[b]]$

2.否则:$[out[a],in[b]]U\{lca\}$

画图理解即可

于是就变成了和普通莫队一样。但需要注意的是这个区间不能拆,所以有一些题做不了...

树分块做法

考虑按某种方法对树分块,最后排序时的关键字改为那个点所属的块的编号

考虑dfs时维护一个栈,进入点x时记下栈中元素个数,每从一个x的子树出来时,判断一下新增的元素个数是否大于$B$(块大小),如果大于的话,把新增的这些点分到同一个块里;出x的时候将x计入栈中。最后剩下的元素扔到最后一个块里

这样可以保证块大小都是$[B,3B]$的

 1 void dfs(int x){
 2     int t=sh;
 3     for(int i=egh[x];i;i=eg[i][1]){
 4         int b=eg[i][0];if(b==fa[x][0]) continue;
 5         fa[b][0]=x,dep[b]=dep[x]+1;dfs(b);
 6         if(sh-t>=blk){
 7             ++bct;
 8             for(;sh>t;sh--) bel[stk[sh]]=bct;
 9         }
10     }
11     stk[++sh]=x;
12 }

然后处理询问的时候,要从(a,b)到(c,b),只要考虑(a,c)路径上的点即可

 

带修莫队

可以将修改视为第三维来做,复杂度$O\left(\frac{qn^2}{B^2}+qB+\frac{n^2}{B}\right)$,B是块大小

$n=q$时可以取$B=n^{\frac{2}{3}}$,复杂度是$O(n^{\frac{5}{3}})$

 

[WC2013]糖果公园

带修树上莫队

  1 #include<bits/stdc++.h>
  2 #include<tr1/unordered_map>
  3 #define CLR(a,x) memset(a,x,sizeof(a))
  4 #define MP make_pair
  5 #define fi first
  6 #define se second
  7 #define oct hakheaw
  8 using namespace std;
  9 typedef long long ll;
 10 typedef unsigned long long ull;
 11 typedef long double ld;
 12 typedef pair<int,int> pa;
 13 const int maxn=1e5+10,Blk=2154;
 14 
 15 inline ll rd(){
 16     ll x=0;char c=getchar();int neg=1;
 17     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
 18     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
 19     return x*neg;
 20 }
 21 
 22 struct Node{
 23     int l,r,x,t,i;
 24     Node(int _l=0,int _r=0,int _x=0,int _t=0,int _i=0){l=_l,r=_r,x=_x,t=_t,i=_i;}
 25 }qr[maxn];
 26 int N,M,Q,val[maxn],eg[maxn*2][2],egh[maxn],ect,col[maxn],lik[maxn];
 27 int dfn[maxn][2],tot,id[maxn*2],fa[maxn][20],dep[maxn];
 28 int cg[maxn][3],oct,qct;
 29 int cnt[maxn];
 30 ll ans[maxn];
 31 bool flag[maxn];
 32 inline void adeg(int a,int b){
 33     eg[++ect][0]=b,eg[ect][1]=egh[a],egh[a]=ect;
 34 }
 35 
 36 void dfs(int x){
 37     dfn[x][0]=++tot;id[tot]=x;
 38     for(int i=0;fa[x][i]&&fa[fa[x][i]][i];i++) fa[x][i+1]=fa[fa[x][i]][i];
 39     for(int i=egh[x];i;i=eg[i][1]){
 40         int b=eg[i][0];if(b==fa[x][0]) continue;
 41         fa[b][0]=x;
 42         dep[b]=dep[x]+1;dfs(b);
 43     }
 44     dfn[x][1]=++tot;id[tot]=x;
 45 }
 46 
 47 inline int getlca(int x,int y){
 48     if(dep[x]<dep[y]) swap(x,y);
 49     for(int i=19;i>=0;i--){
 50         if(fa[x][i]&&dep[fa[x][i]]>=dep[y]) x=fa[x][i];
 51     }
 52     if(x==y) return x;
 53     for(int i=19;i>=0;i--){
 54         if(fa[x][i]&&fa[y][i]&&fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
 55     }
 56     return fa[x][0];
 57 }
 58 
 59 inline bool cmp(Node a,Node b){
 60     return (a.l/Blk==b.l/Blk)?((a.r/Blk==b.r/Blk?(a.i<b.i):a.r<b.r)):a.l<b.l;
 61 }
 62 
 63 inline ll inc(int c){
 64     return 1ll*val[c]*lik[++cnt[c]];
 65 }
 66 
 67 inline ll dec(int c){
 68     return -1ll*val[c]*lik[cnt[c]--];
 69 }
 70 
 71 inline ll deal(int x){
 72     // printf("DEAL:%d\n",x);
 73     flag[x]^=1;
 74     if(flag[x]) return inc(col[x]);
 75     else return dec(col[x]);
 76 }
 77 
 78 inline ll change(int x,int y){
 79     ll re=0;
 80     if(flag[x]) re+=dec(col[x])+inc(y); 
 81     // printf("CG:%d %d %d %d\n",x,y,col[x],re);
 82     col[x]=y;
 83     return re;
 84 }
 85 
 86 int main(){
 87     //freopen("","r",stdin);
 88     N=rd(),M=rd(),Q=rd();
 89     for(int i=1;i<=M;i++) val[i]=rd();
 90     for(int i=1;i<=N;i++) lik[i]=rd();
 91     for(int i=1;i<N;i++){
 92         int a=rd(),b=rd();
 93         adeg(a,b),adeg(b,a);
 94     }
 95     for(int i=1;i<=N;i++) col[i]=rd();
 96     dep[1]=1;dfs(1);
 97     // for(int i=1;i<=tot;i++) printf("~%d %d\n",i,id[i]);
 98     for(int i=1;i<=Q;i++){
 99         int a=rd(),b=rd(),c=rd();
100         if(!a) cg[++oct][0]=b,cg[oct][1]=c,cg[oct][2]=col[b],col[b]=c;
101         else{
102             if(dfn[b][0]>dfn[c][0]) swap(b,c);
103             int lca=getlca(b,c);
104             if(lca==b) qr[++qct]=Node(dfn[b][0],dfn[c][0],lca,oct,i);
105             else qr[++qct]=Node(dfn[b][1],dfn[c][0],lca,oct,i);
106         }
107     }
108     sort(qr+1,qr+qct,cmp);
109     ll now=0;int l=1,r=0,t=oct;
110     for(int i=1;i<=qct;i++){//if(i==2) continue;
111         int x=qr[i].l,y=qr[i].r,z=qr[i].t;
112         while(t<z) ++t,now+=change(cg[t][0],cg[t][1]);
113         while(t>z) now+=change(cg[t][0],cg[t][2]),t--;
114         while(r<y) now+=deal(id[++r]);
115         while(r>y) now+=deal(id[r--]);
116         while(l<x) now+=deal(id[l++]); 
117         while(l>x) now+=deal(id[--l]);
118         ans[qr[i].i]=now;
119         if(qr[i].x!=id[qr[i].l]) ans[qr[i].i]+=deal(qr[i].x),deal(qr[i].x);
120     }
121     for(int i=1;i<=Q;i++) if(ans[i]) printf("%lld\n",ans[i]);
122     return 0;
123 }

 

转载于:https://www.cnblogs.com/Ressed/p/9997453.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值