主席树的各类模板(区间第k大数【动,静】,区间不同数的个数,区间<=k的个数)

取板粗

1.(HDOJ2665)http://acm.hdu.edu.cn/showproblem.php?pid=2665

(POJ2104)http://poj.org/problem?id=2104

(POJ2761)http://poj.org/problem?id=2761

题意:求区间第K大,主席树模板题

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int maxm=3e6+10;
int tot,n,q,m;
int a[maxn],t[maxn];
int c[maxm],lson[maxm],rson[maxm];
int T[maxn];

void init_hash()
{
    for ( int i=1;i<=n;i++ ) t[i]=a[i];
    sort(t+1,t+1+n);
    m=unique(t+1,t+1+n)-(t+1);
}

int hash_(int x)
{
    return lower_bound(t+1,t+1+m,x)-t;
}

void build(int &root,int l,int r)
{
    root=++tot;
    if ( l==r ) return;
    int mid=(l+r)/2;
    build(lson[root],l,mid);
    build(rson[root],mid+1,r);
}

void update(int root,int &rt,int p,int val,int l,int r)
{
    rt=++tot;
    lson[rt]=lson[root],rson[rt]=rson[root];
    c[rt]=c[root]+val;
    if ( l==r ) return;
    int mid=(l+r)/2;
    if ( p<=mid ) update(lson[rt],lson[rt],p,val,l,mid);
    else update(rson[rt],rson[rt],p,val,mid+1,r);
}

int query(int rt_,int rt,int l,int r,int k)
{
    if ( l==r ) return l;
    int mid=(l+r)/2;
    int sum=c[lson[rt_]]-c[lson[rt]];
    if ( sum>=k ) return query(lson[rt_],lson[rt],l,mid,k);
    else return query(rson[rt_],rson[rt],mid+1,r,k-sum);
}

int main()
{
    int Case;
    scanf("%d",&Case);
    while ( Case-- )
    {
        scanf("%d%d",&n,&q);
        tot=0;
        for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]);
        init_hash();
        build(T[0],1,m);
        for ( int i=1;i<=n;i++ )
        {
            int pos=hash_(a[i]);
            update(T[i-1],T[i],pos,1,1,m);
        }
        while ( q-- )
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",t[query(T[r],T[l-1],1,m,k)]);
        }
    }
    return 0;
}

2.(HDOJ4417)http://acm.hdu.edu.cn/showproblem.php?pid=4417

题意:求给定区间<=k的数有多少

分析:在模板上将query部分修改一下即可,对于区间[L,R]来说,只需要将第R颗线段树上的[0,k]区间内的值减去第L-1颗线段树上对应区间即可。离线在线都行,离线做法需要将每次访问的k也添加进入hash数组,而对于在线来说转化后的数转化前相对于给定的k来说只能变小不能变大即可

注意:题目给的区间范围从0开始,要将其转化成从1开始

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+10;
const int maxm=3e6+10;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];

void init_hash()
{
    for ( int i=1;i<=n;i++ ) t[i]=a[i];
    sort(t+1,t+1+n);
    m=unique(t+1,t+1+n)-(t+1);
}

int build(int l,int r)
{
    int root=tot++;
    c[root]=0;
    if ( l!=r )
    {
        int mid=(l+r)/2;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}

int hash_(int x)
{
    return lower_bound(t+1,t+1+m,x)-t;
}

int update(int root,int pos,int val)
{
    int rt=tot++,tmp=rt;
    c[rt]=c[root]+val;
    int l=1,r=m;
    while ( l<r )
    {
        int mid=(l+r)/2;
        if ( pos<=mid )
        {
            lson[rt]=tot++;rson[rt]=rson[root];
            rt=lson[rt];root=lson[root];
            r=mid;
        }
        else
        {
            rson[rt]=tot++;lson[rt]=lson[root];
            rt=rson[rt];root=rson[root];
            l=mid+1;
        }
        c[rt]=c[root]+val;
    }
    return tmp;
}

int query(int lrt,int rrt,int k)
{
    int ret=0;
    int l=1,r=m;
    while ( l<r )
    {
        int mid=(l+r)/2;
        if ( k<=mid )
        {
            r=mid;
            lrt=lson[lrt];
            rrt=lson[rrt];
        }
        else
        {
            ret+=c[lson[rrt]]-c[lson[lrt]];
            l=mid+1;
            lrt=rson[lrt];
            rrt=rson[rrt];
        }
    }
    ret+=c[rrt]-c[lrt];
    return ret;
}

int main()
{
    int Case,h;
    scanf("%d",&Case);
    for ( h=1;h<=Case;h++ )
    {
        scanf("%d%d",&n,&q);
        tot=0;
        for ( int i=1;i<=n;i++ )
        {
            scanf("%d",&a[i]);
            a[i]++;
        }
        init_hash();
        T[0]=build(1,m);
        for ( int i=1;i<=n;i++ )
        {
            int pos=hash_(a[i]);
            T[i]=update(T[i-1],pos,1);
        }
        printf("Case %d:\n",h);
        while ( q-- )
        {
            int l,r,k,p;
            scanf("%d%d%d",&l,&r,&k);
            l++,r++,k++;
            p=hash_(k);
            if ( t[p]>k ) p--;
            if ( p==0 ) printf("0\n");
            else printf("%d\n",query(T[l-1],T[r],p));
        }
    }
    return 0;
}

HDOJ4417(在线)

在线

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+10;
const int maxm=3e6+10;
int n,q,m,tot;
int a[maxn],t[maxn*2],l[maxn],r[maxn],val[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];

void init_hash()
{
    for ( int i=1;i<=n;i++ ) t[i]=a[i];
    for ( int i=1;i<=q;i++ ) t[i+n]=val[i];
    sort(t+1,t+1+n+q);
    m=unique(t+1,t+1+n+q)-(t+1);
}

int build(int l,int r)
{
    int root=tot++;
    c[root]=0;
    if ( l!=r )
    {
        int mid=(l+r)/2;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}

int hash_(int x)
{
    return lower_bound(t+1,t+1+m,x)-t;
}

int update(int root,int pos,int val)
{
    int rt=tot++,tmp=rt;
    c[rt]=c[root]+val;
    int l=1,r=m;
    while ( l<r )
    {
        int mid=(l+r)/2;
        if ( pos<=mid )
        {
            lson[rt]=tot++;rson[rt]=rson[root];
            rt=lson[rt];root=lson[root];
            r=mid;
        }
        else
        {
            rson[rt]=tot++;lson[rt]=lson[root];
            rt=rson[rt];root=rson[root];
            l=mid+1;
        }
        c[rt]=c[root]+val;
    }
    return tmp;
}

int query(int lrt,int rrt,int k)
{
    int ret=0;
    int l=1,r=m;
    while ( l<r )
    {
        int mid=(l+r)/2;
        if ( k<=mid )
        {
            r=mid;
            lrt=lson[lrt];
            rrt=lson[rrt];
        }
        else
        {
            ret+=c[lson[rrt]]-c[lson[lrt]];
            l=mid+1;
            lrt=rson[lrt];
            rrt=rson[rrt];
        }
    }
    ret+=c[rrt]-c[lrt];
    return ret;
}

int main()
{
    int Case,h;
    scanf("%d",&Case);
    for ( h=1;h<=Case;h++ )
    {
        scanf("%d%d",&n,&q);
        tot=0;
        for ( int i=1;i<=n;i++ )
        {
            scanf("%d",&a[i]);
            a[i]++;
        }
        for ( int i=1;i<=q;i++ ) {
            scanf("%d%d%d",&l[i],&r[i],&val[i]);
            l[i]++,r[i]++,val[i]++;
        }
        init_hash();
        T[0]=build(1,m);
        for ( int i=1;i<=n;i++ )
        {
            int pos=hash_(a[i]);
            T[i]=update(T[i-1],pos,1);
        }
        printf("Case %d:\n",h);
        for ( int i=1;i<=q;i++ )
        {
            int L,R,k;
            L=l[i],R=r[i],k=val[i];
            k=hash_(k);
            printf("%d\n",query(T[L-1],T[R],k));
        }
    }
    return 0;
}

HDOJ4417(离线)

离线

3.(SPOJ3267)http://www.spoj.com/problems/DQUERY/

题意:给出一个长度为n 的数列,有q 个询问,每个询问给出数对 [i,j],需要你给出这一段中有多少不同的数字

分析:利用map记录每个数的位置,主席树建新树的时候,如果当前元素出现过,那么把这个元素上次出现的位置减一,然后当前位置加一,如果没出现过就是普通的建树操作。

对于查询[l, r]我们只需要取出第r棵树,然后输出这棵树[l,r]之间的和,因为是按从1到n的顺序插入的,所以每次只需要求>=l的个数即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int maxn=3e4+10;
const int maxm=3e6+10;
int n,q,tot;
int a[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];

int build(int l,int r)
{
    int root=tot++;
    c[root]=0;
    if ( l!=r )
    {
        int mid=(l+r)/2;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}

int update(int root,int pos,int val)
{
    int rt=tot++,tmp=rt;
    c[rt]=c[root]+val;
    int l=1,r=n;
    while ( l<r )
    {
        int mid=(l+r)/2;
        if ( pos<=mid )
        {
            lson[rt]=tot++;rson[rt]=rson[root];
            rt=lson[rt];root=lson[root];
            r=mid;
        }
        else
        {
            rson[rt]=tot++;lson[rt]=lson[root];
            rt=rson[rt];root=rson[root];
            l=mid+1;
        }
        c[rt]=c[root]+val;
    }
    return tmp;
}

int query(int rt,int lpos)
{
    int ret=0;
    int l=1,r=n;
    while ( lpos>l )
    {
        int mid=(l+r)/2;
        if ( lpos<=mid )
        {
            r=mid;
            ret+=c[rson[rt]];
            rt=lson[rt];
        }
        else
        {
            rt=rson[rt];
            l=mid+1;
        }
    }
    return ret+c[rt];
}

int main()
{
    int Case;
    while ( scanf("%d",&n)!=EOF )
    {
        tot=0;
        for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]);
        T[0]=build(1,n);
        map<int,int>mp;
        for ( int i=1;i<=n;i++ )
        {
            if ( mp.find(a[i])!=mp.end() )
            {
                int tmp=update(T[i-1],mp[a[i]],-1);
                T[i]=update(tmp,i,1);
            }
            else T[i]=update(T[i-1],i,1);
            mp[a[i]]=i;
        }
        scanf("%d",&q);
        while ( q-- )
        {
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",query(T[r],l));
        }
    }
    return 0;
}

SPOJ3267

4.(ZOJ2112)http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112

题意:给定一串序列,有两种操作,一种是求区间[l,r]第k大,另外一种是将a[i]=t

带修改的主席树

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=60010;
const int maxm=2500010;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];
int S[maxn];
struct Query{
    int kind;
    int l,r,k;
}query[10010];

void init_hash(int k)
{
    sort(t+1,t+k+1);
    m=unique(t+1,t+k+1)-(t+1);
}

int hash_(int x)
{
    return lower_bound(t+1,t+m+1,x)-t;
}

int build(int l,int r)
{
    int root=tot++;
    c[root]=0;
    if ( l!=r )
    {
        int mid=(l+r)/2;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    return root;
}

int update(int root,int pos,int val)
{
    int rt=tot++,tmp=rt;
    c[rt]=c[root]+val;
    int l=1,r=m;
    while ( l<r )
    {
        int mid=(l+r)/2;
        if ( pos<=mid )
        {
            lson[rt]=tot++;rson[rt]=rson[root];
            rt=lson[rt];root=lson[root];
            r=mid;
        }
        else
        {
            rson[rt]=tot++;lson[rt]=lson[root];
            rt=rson[rt];root=rson[root];
            l=mid+1;
        }
        c[rt]=c[root]+val;
    }
    return tmp;
}

int lowbit(int x)
{
    return x&(-x);
}

int used[maxn];
void add(int x,int pos,int val)
{
    while ( x<=n )
    {
        S[x]=update(S[x],pos,val);
        x+=lowbit(x);
    }
}

int sum(int x)
{
    int ret=0;
    while ( x>0 )
    {
        ret+=c[lson[used[x]]];
        x-=lowbit(x);
    }
    return ret;
}

int Q(int left,int right,int k)
{
    int lrt=T[left];
    int rrt=T[right];
    int l=1,r=m;
    for ( int i=left;i>0;i-=lowbit(i)) used[i]=S[i];
    for ( int i=right;i>0;i-=lowbit(i)) used[i]=S[i];
    while ( l<r )
    {
        int mid=(l+r)/2;
        int tmp=sum(right)-sum(left)+c[lson[rrt]]-c[lson[lrt]];
        if ( tmp>=k )
        {
            r=mid;
            for ( int i=left;i>0;i-=lowbit(i)) used[i]=lson[used[i]];
            for ( int i=right;i>0;i-=lowbit(i)) used[i]=lson[used[i]];
            lrt=lson[lrt];
            rrt=lson[rrt];
        }
        else
        {
            l=mid+1;
            k-=tmp;
            for ( int i=left;i>0;i-=lowbit(i)) used[i]=rson[used[i]];
            for ( int i=right;i>0;i-=lowbit(i)) used[i]=rson[used[i]];
            lrt=rson[lrt];
            rrt=rson[rrt];
        }
    }
    return l;
}

int main()
{
    int Case;
    scanf("%d",&Case);
    while ( Case-- )
    {
        scanf("%d%d",&n,&q);
        tot=0;
        m=0;
        for ( int i=1;i<=n;i++ )
        {
            scanf("%d",&a[i]);
            t[++m]=a[i];
        }
        char op[10];
        for ( int i=0;i<q;i++ )
        {
            scanf("%s",op);
            if ( op[0]==‘Q‘ )
            {
                query[i].kind=0;
                scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);
            }
            else
            {
                query[i].kind=1;
                scanf("%d%d",&query[i].l,&query[i].r);
                t[++m]=query[i].r;
            }
        }
        init_hash(m);
        T[0]=build(1,m);
        for ( int i=1;i<=n;i++ )
        {
            int pos=hash_(a[i]);
            T[i]=update(T[i-1],pos,1);
        }
        for ( int i=1;i<=n;i++ ) S[i]=T[0];
        for ( int i=0;i<q;i++ )
        {
            if ( query[i].kind==0 ) printf("%d\n",t[Q(query[i].l-1,query[i].r,query[i].k)]);
            else
            {
                add(query[i].l,hash_(a[query[i].l]),-1);
                add(query[i].l,hash_(query[i].r),1);
                a[query[i].l]=query[i].r;
            }
        }
    }
    return 0;
}

ZOJ2112

5.(HDOJ4348)http://acm.hdu.edu.cn/showproblem.php?pid=4348

题意:给出一段长度为n的序列,有4种操作。初始时,时间戳=0

a.C l r d [l,r]区间内的数+d,时间戳++

b.Q l r 求当前时间戳下[l,r]区间的和

c.H l r t 求时间戳=t下[l,r]区间的和

d.B t  时间戳=t

分析:推荐两个讲解较为详细的博客https://blog.csdn.net/glqac/article/details/45103859

https://blog.csdn.net/kirito16/article/details/47266801

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int maxm=3e6+10;
int n,q,tot;
int a[maxn];
int T[maxn],lson[maxm],rson[maxm];
ll sum[maxm],add[maxm];

int build(int l,int r)
{
    int root=tot++;
    add[root]=0;
    if ( l!=r )
    {
        int mid=(l+r)/2;
        lson[root]=build(l,mid);
        rson[root]=build(mid+1,r);
    }
    else
    {
        scanf("%lld",&sum[root]);
        return root;
    }
    sum[root]=sum[lson[root]]+sum[rson[root]];
    return root;
}

void pushup(int rt,int len)
{
    sum[rt]=sum[lson[rt]]+sum[rson[rt]]+add[lson[rt]]*(len-len/2)+add[rson[rt]]*(len/2);
}

int A,B;
ll val;

int update(int root,int l,int r)
{
    int rt=tot++;
    add[rt]=add[root];
    if ( A<=l && r<=B )
    {
        sum[rt]=sum[root];
        add[rt]=add[root]+val;
        lson[rt]=lson[root];
        rson[rt]=rson[root];
        return rt;
    }
    int mid=(l+r)/2;
    if ( A<=mid ) lson[rt]=update(lson[root],l,mid);
    else lson[rt]=lson[root];
    if ( B>mid ) rson[rt]=update(rson[root],mid+1,r);
    else rson[rt]=rson[root];
    pushup(rt,r-l+1);
    return rt;
}

ll query(int root,int l,int r,ll add_)
{
    if ( A<=l && r<=B ) return sum[root]+(add_+add[root])*(r-l+1);
    ll ans=0;
    int mid=(l+r)/2;
    if ( A<=mid ) ans+=query(lson[root],l,mid,add[root]+add_);
    if ( B>mid ) ans+=query(rson[root],mid+1,r,add[root]+add_);
    return ans;
}

int main()
{
    char op[5];
    int now,Case=0;
    while ( scanf("%d%d",&n,&q)!=EOF )
    {
        if ( Case!=0 ) printf("\n");
        Case++;
        tot=0;
        T[0]=build(1,n);
        now=0;
        while ( q-- )
        {
            ll ans;
            int k;
            scanf("%s",op);
            if ( op[0]==‘C‘ )
            {
                scanf("%d%d%lld",&A,&B,&val);
                T[now+1]=update(T[now],1,n);
                now++;
            }
            else if ( op[0]==‘Q‘ )
            {
                scanf("%d%d",&A,&B);
                ans=query(T[now],1,n,0);
                printf("%lld\n",ans);
            }
            else if ( op[0]==‘H‘ )
            {
                scanf("%d%d%d",&A,&B,&k);
                ans=query(T[k],1,n,0);
                printf("%lld\n",ans);
            }
            else if ( op[0]==‘B‘ )
            {
                scanf("%d",&k);
                now=k;
                tot=T[now+1];
            }
        }
    }
    return 0;
}

HDOJ4348

原文地址:https://www.cnblogs.com/shuaihui520/p/9742271.html

时间: 2024-10-13 22:27:57

主席树的各类模板(区间第k大数【动,静】,区间不同数的个数,区间<=k的个数)的相关文章

1540: 第k大数 两个数组元素相乘后的第k大( 二分答案 + 输入输出优化 )

1540: 第k大数 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 104 Solved: 6 [Submit][Status][Web Board] Description 有两个序列a,b,它们的长度分别为n和m,那么将两个序列中的元素对应相乘后得到的n*m个元素从大到小排列后的第k个元素是什么? Input 输入的第一行为一个正整数T (T<=10),代表一共有T组测试数据. 每组测试数据的第一行有三个正整数n,m和k(1<=n, m&l

线段树之各类模板

线段树的认识,可以参考哔哔丽丽的视频. (1)单点曽减,区间求和 飞翔 #include<stdio.h> #define lson l,m,rt<<1///左儿子 #define rson m+1,r,rt<<1|1///右儿子 const int maxn = 55555; int sum[maxn<<2]; void PushUP(int rt)///上一个的值由两个儿子相加 { sum[rt]=sum[rt<<1]+sum[rt<&

少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小

少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小 有一道题(BZOJ 1901)是这样的:n个数,m个询问,询问有两种:修改某个数/询问区间第k小. 不带修改的区间第k小用主席树很好写,不会的同学可以看一下这个. 加上修改怎么做呢?我们可以用数学老师成天讲的类比思想: 可以发现,不修改的区间k小问题中,每加入一个原序列中的数,对应的主席树在上一个的基础上进行修改,而查询的时候用右端点主席树减去左端点左边的主席树.这样的操作就像是维护前缀和:每次加入一个元素的时候,sum[i] =

我想要打一个主席树

这里提供了静态主席树及动态主席树(树状数组套主席树)的模板. 分别应用于静态区间第k小及动态区间第k小. 静态: #include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC target("avx&q

[知识学习] 主席树

这两天学习了主席树,基本上搞懂了主席树是怎么操作的 主席树,是一种可持久化线段树.最简单的操作就是维护静态区间第 \(k\) 小 主席树通过维护历史版本,实现查询区间的有关操作 主席树的原理 假设现在有这么一个序列:\(4,1,3,5,2\) 问如何求出区间[1,3]内大小为第二的数? 利用大眼观察法,很显然是3 那么让计算机去怎么实现呢?它又没有眼睛 对于这个序列,我们可以先建一颗空的权值线段树,命名为"树0"(方便后面的使用),如图: 别告诉我你不知道什么是权值线段树,自己去百度:

【bzoj4408】[Fjoi 2016]神秘数 主席树

题目描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1+1+14 = 45 = 4+16 = 4+1+17 = 4+1+1+18无法表示为集合S的子集的和,故集合S的神秘数为8.现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数. 输入 第一行一个整数n,表示数字个数.第二行n个整数,从1编

SPOJ_10628_Count_on_a_Tree(主席树+Tarjan)

描述 http://www.spoj.com/problems/COT/ 给出一棵n个节点的树,树上每一个节点有权值.m次询问,求书上u,v路径中第k小的权值. 分析 POJ_2104_Kth(主席树) 现在是把原来的问题搬到树上去了.首先我们肯定要求lca,新学了Tarjan的离线算法. 每一个点建立到根节点的主席树,这样最后的结果就是u+v-2*lca,如果lca在所要求的区间内,还要再加上lca.(或者u+v-lca-p[lca]). 自己理解一下吧...挺简单的. 1 #include

【CQOI2015】 任务查询系统 (主席树)

luogu & bzoj 题目描述 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个)的优先级之和是多少.特别的

SPOJ 3267. D-query (主席树)

A - D-query Time Limit:1500MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the nu