莫队

ooliver的头像 发布于 13 小时前 11 次阅读 OI


P2709 【模板】莫队 / 小B的询问

模板,直接看代码吧:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e4+5;
int n,m,k,siz,sum;
int a[N],c[N],ans[N];
struct ask {
    int l,r,id;
    bool operator<(const ask &a)const {
        if ((l-1)/siz==(a.l-1)/siz) return r<a.r;
        return l/siz<a.l/siz;
    }
}q[N];
void add(int x) {
    sum-=c[x]*c[x];
    c[x]++;
    sum+=c[x]*c[x];
}
void del(int x) {
    sum-=c[x]*c[x];
    c[x]--;
    sum+=c[x]*c[x];
}
signed main(){
    cin>>n>>m>>k;
    siz=sqrt(n);
    for (int i=1;i<=n;i++) cin>>a[i];
    for (int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int l=1,r=0;
    for (int i=1;i<=m;i++) {
        while (l>q[i].l) l--,add(a[l]);
        while (r<q[i].r) r++,add(a[r]);
        while (l<q[i].l) del(a[l]),l++;
        while (r>q[i].r) del(a[r]),r--;
        ans[q[i].id]=sum;
    }
    for (int i=1;i<=m;i++) cout<<ans[i]<<endl;
    return 0;
}

P1494 [国家集训队] 小 Z 的袜子

考虑枚举每种颜色的式子,会发现展开后要维护的就是区间种类平方和,与板子一样,代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N=5e4+5;
int n,m,siz,sum;
int a[N],c[N],ans1[N],ans2[N];

struct ask {
    int l,r,id;
    bool operator<(const ask &a)const {
        if ((l-1)/siz==(a.l-1)/siz) return r<a.r;
        return l/siz<a.l/siz;
    }
}q[N];

void add(int x) {
    sum-=c[x]*c[x];
    c[x]++;
    sum+=c[x]*c[x];
}

void del(int x) {
    sum-=c[x]*c[x];
    c[x]--;
    sum+=c[x]*c[x];
}

signed main(){
    cin>>n>>m;
    siz=sqrt(n);
    for (int i=1;i<=n;i++) cin>>a[i];
    for (int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int l=1,r=0;
    for (int i=1;i<=m;i++) {
        if(q[i].l==q[i].r){
            ans1[q[i].id]=0,ans2[q[i].id]=1;
            continue;
        }
        while (l>q[i].l) l--,add(a[l]);
        while (r<q[i].r) r++,add(a[r]);
        while (l<q[i].l) del(a[l]),l++;
        while (r>q[i].r) del(a[r]),r--;
        int len=q[i].r-q[i].l+1;
        int fz=sum-len,fm=len*(len-1);
        ans1[q[i].id]=fz/__gcd(fz,fm),ans2[q[i].id]=fm/__gcd(fz,fm);
    }
    for (int i=1;i<=m;i++) cout<<ans1[i]<<"/"<<ans2[i]<<"\n";
    return 0;
}

P4462 [CQOI2018] 异或序列

异或前缀和一下,式子就能转化成两个下标之间的关系。

用一个桶装每个值的数量,莫队移动区间计算即可。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+5;
int n,m,k,siz,sum;
int a[N],tag[N],ans[N];

struct ask {
    int l,r,id;
    bool operator<(const ask &a)const {
        if ((l-1)/siz==(a.l-1)/siz) return r<a.r;
        return l/siz<a.l/siz;
    }
}q[N];

void add(int x) {
    sum+=tag[k^x];
    tag[x]++;
}

void del(int x) {
    tag[x]--;
    sum-=tag[k^x];
}

signed main(){
    cin>>n>>m>>k;
    siz=sqrt(n),tag[0]=1;
    for (int i=1;i<=n;i++) cin>>a[i],a[i]^=a[i-1];
    for (int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int l=0,r=0;
    tag[0]=1;
    for (int i=1;i<=m;i++) {
        while (l<q[i].l-1) del(a[l]),l++;
        while (r>q[i].r) del(a[r]),r--;
        while (l>q[i].l-1) l--,add(a[l]);
        while (r<q[i].r) r++,add(a[r]);
        ans[q[i].id]=sum;
    }
    for (int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P3709 大爷的字符串题

发现最优时是把序列变成若干个连续上升序列拼接,而数量就应该是区间众数。

所以用莫队维护区间众数即可。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=2e5+5;
int n,m,k,siz,sum;
int a[N],b[N],cnt[N],t[N],ans[N];

struct ask {
    int l,r,id;
    bool operator<(const ask &a)const {
        if ((l-1)/siz==(a.l-1)/siz) return r<a.r;
        return l/siz<a.l/siz;
    }
}q[N];

void add(int x) {
    t[cnt[x]]--;
    t[++cnt[x]]++;
    sum=max(sum,cnt[x]);
}

void del(int x) {
    t[cnt[x]]--;
    if(cnt[x]==sum&&!t[cnt[x]]) sum--;
    t[--cnt[x]]++;
}

signed main(){
    cin>>n>>m;
    siz=sqrt(n);
    for (int i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+n,a[i])-b;
    for (int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int l=1,r=0;
    for (int i=1;i<=m;i++) {
        while (l>q[i].l) l--,add(a[l]);
        while (r<q[i].r) r++,add(a[r]);
        while (l<q[i].l) del(a[l]),l++;
        while (r>q[i].r) del(a[r]),r--;
        ans[q[i].id]=sum;
    }
    for (int i=1;i<=m;i++) cout<<-ans[i]<<"\n";
    return 0;
}

P5906 【模板】回滚莫队&不删除莫队

也是模板,代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=4e5+5;
const int M=sqrt(N)+5;
int n,m,k,siz,now;
int st[M],ed[M];
int a[N],b[N],qz[N],hz[N],hz2[N],cnt[N],ans[N],cnt2[N];

struct ask{
    int l,r,id;
    bool operator<(const ask &a)const{
        if((l-1)/siz+1==(a.l-1)/siz+1) return r<a.r;
        return (l-1)/siz<(a.l-1)/siz;
    }
}q[N];

void add(int x){
    qz[a[x]]=min(qz[a[x]],x);
    hz[a[x]]=max(hz[a[x]],x);
    now=max(now,hz[a[x]]-qz[a[x]]);
}

void del(int x){hz2[a[x]]=0;}

int get(int l,int r){
    int res=0;
    for(int i=l;i<=r;i++) cnt[a[i]]=0;
    for(int i=l;i<=r;i++){
        if(!cnt[a[i]]) cnt[a[i]]=i;
        res=max(res,i-cnt[a[i]]);
    }
    return res;
}

signed main(){
    memset(qz,0x3f,sizeof qz);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
    siz=sqrt(n*1.0),k=ceil(n*1.0/(siz*1.0));
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+n,a[i])-b;
    cin>>m;
    for(int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int id=0;
    for(int i=1;i<=k;i++){
        memset(qz,0x3f,sizeof qz);
        memset(hz,0,sizeof hz);
        now=0;
        int L=(i-1)*siz+1,R=min(i*siz,n);
        int l=R+1,r=R;
        while(q[id+1].l>=L&&q[id+1].l<=R&&id+1<=m){
            id++;
            if(q[id].r<=R){
                ans[q[id].id]=get(q[id].l,q[id].r);
                continue;
            }
            while(r<q[id].r) r++,add(r);
            int tmp=now;
            int tmp2=0;
            while(l>q[id].l){
                l--;
                if(!hz2[a[l]]) hz2[a[l]]=l;
                tmp2=max(tmp2,max(hz2[a[l]],hz[a[l]])-l);
            }
            ans[q[id].id]=max(now,tmp2);
            now=tmp;
            while(l<=R) del(l),l++;
        }
    }
    for (int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P14420 [JOISC 2014] 历史的研究 / Historical Research

发现莫队时增加一个元素很好写,但删一个就不好维护了,所以考虑回滚莫队。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+5;
const int M=sqrt(N)+5;
int n,m,k,siz,now;
int st[M],ed[M];
int a[N],b[N],cnt[N],ans[N],cnt2[N];

struct ask{
    int l,r,id;
    bool operator<(const ask &a)const{
        if((l-1)/siz+1==(a.l-1)/siz+1) return r<a.r;
        return (l-1)/siz<(a.l-1)/siz;
    }
}q[N];

void add(int x){
    cnt[a[x]]++;
    now=max(now,cnt[a[x]]*b[a[x]]);
}

void del(int x){cnt[a[x]]--;}

int get(int l,int r){
    int res=0;
    for(int i=l;i<=r;i++) cnt2[a[i]]=0;
    for(int i=l;i<=r;i++){
        cnt2[a[i]]++;
        res=max(res,cnt2[a[i]]*b[a[i]]);
    }
    return res;
}

signed main(){
    cin>>n>>m;
    siz=sqrt(n*1.0),k=ceil(n*1.0/(siz*1.0));
    for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+n,a[i])-b;
    for(int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int id=0;
    for(int i=1;i<=k;i++){
        memset(cnt,0,sizeof cnt);
        now=0;
        int L=(i-1)*siz+1,R=min(i*siz,n);
        int l=R+1,r=R;
        while(q[id+1].l>=L&&q[id+1].l<=R&&id+1<=m){
            id++;
            if(q[id].r<=R){
                ans[q[id].id]=get(q[id].l,q[id].r);
                continue;
            }
            while(r<q[id].r) r++,add(r);
            int tmp=now;
            while(l>q[id].l) l--,add(l);
            ans[q[id].id]=now;
            now=tmp;
            while(l<=R) del(l),l++;
        }
    }
    for (int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P4137 Rmq Problem / mex

用 bitset 维护当前信息,用 _Find_first() 函数求 mex 即可。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=2e5+5;
int n,m,k,siz;
int a[N],sum[N],ans[N];
bitset<N> now;

struct ask{
    int l,r,id;
    bool operator<(const ask &a)const{
        if((l-1)/siz==(a.l-1)/siz) return r<a.r;
        return l/siz<a.l/siz;
    }
}q[N];

void add(int x){
    sum[x]++;
    now[x]=1;
}

void del(int x){
    sum[x]--;
    if(!sum[x]) now[x]=0;
}

signed main(){
    cin>>n>>m;
    siz=sqrt(n);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while (l>q[i].l) l--,add(a[l]);
        while (r<q[i].r) r++,add(a[r]);
        while (l<q[i].l) del(a[l]),l++;
        while (r>q[i].r) del(a[r]),r--;
        ans[q[i].id]=(~now)._Find_first();
    }
    for (int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P8078 [WC2022] 秃子酋长

用链表维护前后关系,这时删除一个元素比较好实现,但增加一个元素并不好实现,所以考虑回滚莫队。

在莫队回溯时用栈维护删除元素顺序,倒序恢复即可。

代码:

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;
int n,m,siz,tp;
long long now;
int a[N],inv[N],stk[N],lst[N],nxt[N];
long long ans[N];

struct ask{
    int l,r,id;
    bool operator<(const ask &a)const{
        int bl=(l-1)/siz,ba=(a.l-1)/siz;
        if(bl!=ba)return bl<ba;
        return r>a.r;
    }
}q[N];

inline void del(int x,int f){
    int v=a[x],p=lst[v],s=nxt[v];
    if(f) stk[++tp]=x;
    if(p>=1&&s<=n) now+=abs(inv[p]-inv[s]);
    if(p>=1) now-=abs(inv[p]-inv[v]);
    if(s<=n) now-=abs(inv[v]-inv[s]);
    nxt[p]=s;lst[s]=p;
}

inline void back(int x){
    int v=a[x],p=lst[v],s=nxt[v];
    if(p>=1&&s<=n) now-=abs(inv[p]-inv[s]);
    if(p>=1) now+=abs(inv[p]-inv[v]);
    if(s<=n) now+=abs(inv[v]-inv[s]);
    nxt[p]=v,lst[s]=v;
}

signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i],inv[a[i]]=i;
    siz=n/sqrt(m);
    for(int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
    sort(q+1,q+m+1);
    for(int i=0;i<=n+1;i++) lst[i]=i-1,nxt[i]=i+1;
    for(int i=1;i<n;i++) now+=abs(inv[i]-inv[i+1]);
    int cur=1,L=1;
    for(int i=0;i<=(n-1)/siz;i++){
        int qL=i*siz+1;
        while(L<qL) del(L++,0);
        long long now1=now;
        int r=n;
        tp=0;
        while(cur<=m&&(q[cur].l-1)/siz==i){
            while(r>q[cur].r) del(r--,1);
            long long tmp=now;
            int l=L,rtp=tp;
            while(l<q[cur].l) del(l++,1);
            ans[q[cur].id]=now;
            while(tp>rtp) back(stk[tp--]);
            now=tmp,cur++;
        }
        while(tp) back(stk[tp--]);
        now=now1;
    }
    for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P4074 [WC2013] 糖果公园

树上带修莫队。

这里使用括号序列把树映射成序列,主要原理是记录 dfs 时进入和离开每个节点的顺序。

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=2e5+5;
vector<int> g[N];
int fa[N],son[N],top[N],siz[N],dep[N];
int idx,st[N],ed[N],a[N];
int n,m,k,bs,v[N],w[N],c[N];
int pos[N],nc[N],vis[N],cnt[N],ans[N],sum,num;

struct node{
    int l,r,t,id,lc;
    bool operator<(const node b){
        if(l/bs!=b.l/bs) return l<b.l;
        if(r/bs!=b.r/bs) return r<b.r;
        return t<b.t;
    }
}q[N];

void dfs1(int u,int f){
    fa[u]=f,siz[u]=1,dep[u]=dep[f]+1;
    for(int v:g[u]){
        if(v==f) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int topf){
    st[u]=++idx,top[u]=topf,a[idx]=u;
    if(son[u]) dfs2(son[u],topf);
    for(int v:g[u]){
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
    ed[u]=++idx,a[idx]=u;
}

int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    return (dep[x]<dep[y]?x:y);
}

void add(int x){
    vis[x]^=1;
    if(vis[x]) sum+=w[++cnt[c[x]]]*v[c[x]];
    else sum-=w[cnt[c[x]]--]*v[c[x]];
}

void solve(){
    bs=pow(2*n,2.0/3);
    sort(q+1,q+1+num);
    int l=1,r=0,t=0;
    for(int i=1;i<=num;i++){
        while(l>q[i].l) l--,add(a[l]);
        while(r<q[i].r) r++,add(a[r]);
        while(l<q[i].l) add(a[l]),l++;
        while(r>q[i].r) add(a[r]),r--;
        while(t<q[i].t){
            t++;
            if(vis[pos[t]]){
                add(pos[t]);
                swap(c[pos[t]],nc[t]);
                add(pos[t]);
            }
            else swap(c[pos[t]],nc[t]);
        }
        while(t>q[i].t){
            if(vis[pos[t]]){
                add(pos[t]);
                swap(c[pos[t]],nc[t]);
                add(pos[t]);
            }
            else swap(c[pos[t]],nc[t]);
            t--;
        }
        ans[q[i].id]=sum;
        if(q[i].lc) ans[q[i].id]+=w[cnt[c[q[i].lc]]+1]*v[c[q[i].lc]];
    }
}

signed main(){
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++) cin>>v[i];
    for(int i=1;i<=n;i++) cin>>w[i];
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs1(1,0);
    dfs2(1,1);
    for(int i=1;i<=n;i++) cin>>c[i];
    for(int i=1,t=0;i<=k;i++){
        int ty,x,y;
        cin>>ty>>x>>y;
        if(ty==1){
            int lc=lca(x,y);
            q[++num].t=t;
            q[num].id=num;
            if(st[x]>st[y]) swap(x,y);
            if(lc==x){
                q[num].l=st[x];
                q[num].r=st[y];
            }
            else{
                q[num].l=ed[x];
                q[num].r=st[y];
                q[num].lc=lc;
            }
        }
        else{
            pos[++t]=x;
            nc[t]=y;
        }
    }
    solve();
    for(int i=1;i<=num;i++) cout<<ans[i]<<"\n";
    return 0;
}

P5268 [SNOI2017] 一个简单的询问

把函数拆成两个差分的前缀和函数相乘,一个询问拆成四个询问,这样就很好写了。

详细实现原理很简单,可以看代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=5e5+5;
int n,m,idx,siz,now;
int a[N],suml[N],sumr[N],ans[N];

struct ask{
    int l,r,tag,id;
    bool operator<(const ask &a)const{
        if ((l-1)/siz==(a.l-1)/siz) return r<a.r;
        return l/siz<a.l/siz;
    }
}q[N];

signed main(){
    cin>>n;
    siz=sqrt(n);
    for(int i=1;i<=n;i++) cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;i++){
        int l1,l2,r1,r2;
        cin>>l1>>r1>>l2>>r2;
        q[++idx]=(ask){r1,r2,1,i};
        q[++idx]=(ask){r1,l2-1,-1,i};
        q[++idx]=(ask){r2,l1-1,-1,i};
        q[++idx]=(ask){l1-1,l2-1,1,i};
    }
    sort(q+1,q+1+idx);
    int l=0,r=0;
    for(int i=1;i<=idx;i++){
        while(l<q[i].l) l++,suml[a[l]]++,now+=sumr[a[l]];
        while(l>q[i].l) suml[a[l]]--,now-=sumr[a[l]],l--;
        while(r<q[i].r) r++,sumr[a[r]]++,now+=suml[a[r]];
        while(r>q[i].r) sumr[a[r]]--,now-=suml[a[r]],r--;
        ans[q[i].id]+=now*q[i].tag;
    }
    for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

P4689 [Ynoi Easy Round 2016] 这是我自己的发明

和上一题很像,难点是换根。

考虑不换根就直接 dfs 序,子树是连续的。

我们钦定一个根节点 1,要换的根 xx 与查询的节点 uu 在以 1 为根时有两种关系。

  1. xx 不在 uu 子树内,那 uu 的子树其实不会变。
  2. xxuu 子树内,那 uu 的子树就变成了同时为 uu 的儿子和 xx 的祖先的点子树外的所有点。

根据以上性质写就行了,代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+5,M=2e6+5;
int n,m,rt=1,idx,siz,now,id;
int fa[N][21],d[N],son[N],dfn[N],low[N],tim;
int a[N],a1[N],b[N],suml[N],sumr[N],ans[M];
vector<int> g[N];

struct ask{
    int l,r,tag,id;
    bool operator<(const ask &o)const{
        int bl=l/siz,obl=o.l/siz;
        if(bl!=obl) return bl<obl;
        return (bl&1)?(r<o.r):(r>o.r);
    }
}q[M*4];

struct path{int u,v;};

void add(int l1,int r1,int l2,int r2,int i){
    auto push=[&](int L,int R,int T){
        if(L<=0||R<=0) return;
        if(L>R) swap(L,R);
        q[++idx]={L,R,T,i};
    };
    push(r1,r2,1);
    push(r1,l2-1,-1);
    push(r2,l1-1,-1);
    push(l1-1,l2-1,1);
}

void dfs(int u,int f){
    dfn[u]=++tim,fa[u][0]=f,d[u]=d[f]+1,son[u]=1;
    for(int i=1;i<=19;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int v:g[u]) if(v!=f) dfs(v,u),son[u]+=son[v];
    low[u]=tim;
}

int find(int u,int k){
    for(int i=19;i>=0;i--) if(k&(1<<i)) u=fa[u][i];
    return u;
}

vector<path> get(int u){
    if(rt==u) return {{1,n}};
    if(dfn[rt]<dfn[u]||dfn[rt]>low[u]) return {{dfn[u],low[u]}};
    int f=find(rt,d[rt]-d[u]-1);
    vector<path> res;
    if(dfn[f]>1) res.push_back({1,dfn[f]-1});
    if(low[f]<n) res.push_back({low[f]+1,n});
    return res;
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a1[i],b[i]=a1[i];
    sort(b+1,b+1+n);
    int tot=unique(b+1,b+1+n)-b-1;
    for(int i=1;i<=n;i++) a1[i]=lower_bound(b+1,b+tot,a1[i])-b;
    for(int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        g[u].push_back(v);g[v].push_back(u);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++) a[dfn[i]]=a1[i];
    for(int i=1;i<=m;i++){
        int op,x,y;cin>>op;
        if(op==1) cin>>rt;
        else{
            id++;cin>>x>>y;
            vector<path> x1=get(x),y1=get(y);
            for(auto &i:x1) for(auto &j:y1) add(i.u,i.v,j.u,j.v,id);
        }
    }
    siz=max(1LL,(int)(n/sqrt(max(1LL,idx))));
    sort(q+1,q+1+idx);
    int cl=0,cr=0;
    for(int i=1;i<=idx;i++){
        while(cl<q[i].l) cl++,suml[a[cl]]++,now+=sumr[a[cl]];
        while(cl>q[i].l) now-=sumr[a[cl]],suml[a[cl]]--,cl--;
        while(cr<q[i].r) cr++,sumr[a[cr]]++,now+=suml[a[cr]];
        while(cr>q[i].r) now-=suml[a[cr]],sumr[a[cr]]--,cr--;
        ans[q[i].id]+=now*q[i].tag;
    }
    for(int i=1;i<=id;i++) cout<<ans[i]<<"\n";
    return 0;
}