bzoj1878

主席树

设nt[i]表示v[i]这个数下一个出现的位置,没有的话就是n+1

题目就转化为求区间[l,r]之间nt[i]>=r+1的数字个数

这就可以弄个权值线段树,询问区间和

不知道为什么我的动态开点开了1e7会还不够(好像是爆数组TLE)

gyz大佬帮我把我的主席树改成指针写法才A了qwq

/**************************************************************

    Problem: 1878

    User: syh0313

    Language: C++

    Result: Accepted

    Time:2924 ms

    Memory:22068 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

#include <map>

#define lch n->lc

#define rch n->rc

using namespace std;

const int maxn=500010;

int n,m,v[maxn],nt[maxn];

struct da{da*lc,*rc;int sum;

    da(){lc=rc=0;sum=0;}

};da*root[maxn];

map<int,int>st;

void build_tree(da*&n,int l,int r)

{

    if(!n)n=new da;

    n->sum=0; if (l==r) return;

    int mid=(l+r)>>1;

    build_tree(lch,l,mid); build_tree(rch,mid+1,r);

}

void updata(da*&n){n->sum=lch->sum+rch->sum;}

void tree_add(da*&old,da*&n,int l,int r,int lc)

{

    n=new da;

    if (l==r && l==lc) {n->sum=old->sum+1; return;}

    int mid=(l+r)>>1;

    if (lc<=mid) {rch=old->rc; tree_add(old->lc,lch,l,mid,lc);}

     else {lch=old->lc; tree_add(old->rc,rch,mid+1,r,lc);}

    updata(n);

}

int qury(da*&old,da*&n,int L,int R,int l,int r)

{

    if (L==l && R==r) return n->sum-old->sum;

    int mid=(L+R)>>1;

    if (r<=mid) return qury(old->lc,lch,L,mid,l,r);

    else if (l>=mid+1) return qury(old->rc,rch,mid+1,R,l,r);

return qury(old->lc,lch,L,mid,l,mid)+qury(old->rc,rch,mid+1,R,mid+1,r);

}

int main()

{

    scanf("%d",&n);

    for (int i=1;i<=n;i++) scanf("%d",&v[i]),st[v[i]]=n+1;

    for (int i=n;i>=1;i--) nt[i]=st[v[i]],st[v[i]]=i;

    build_tree(root[0],1,n+1);

    for (int i=1;i<=n;i++) {

     tree_add(root[i-1],root[i],1,n+1,nt[i]);

    }

    int num; scanf("%d",&num);

    for (int i=1;i<=num;i++)

    {

        int l,r; scanf("%d%d",&l,&r);

        printf("%d\n",qury(root[l-1],root[r],1,n+1,r+1,n+1));

    }

return 0;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值