【线段树和树状数组】洛谷P-1972HH的项链

原题链接

先是树状数组代码
在线做法应该是非常耗时的
所以这里考虑离线做法
按照每次询问的r边界进行从小到大的排序
就可以处理了

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int a[N];
int tree[N];
int flag[N];
int n;
int res[N];
struct node{
    int l,r;
    int id;
    int pos;
    bool operator < (const node &a)const{
        return r<a.r;
    }
}ask[N];

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

void add(int nowv,int now)
{
    while(nowv<=n){
        tree[nowv]+=now;
        nowv+=lowbit(nowv);
    }
}

int sum(int x)
{
    int tmp=0;
    while(x!=0){
        tmp+=tree[x];
        x-=lowbit(x);
    }
    return tmp;
}

int main()
{

    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
    }
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&ask[i].l,&ask[i].r);
        ask[i].pos=i;
    }
    sort(ask+1,ask+m+1);
    int las_r=1;
    for(int i=1;i<=m;i++){
        int l=ask[i].l;
        int r=ask[i].r;
        for(int j=las_r;j<=r;j++){
            if(flag[a[j]]){
                add(flag[a[j]],-1);
            }
            add(j,1);
            flag[a[j]]=j;
        }
        res[ask[i].pos]=sum(r)-sum(l-1);
        las_r=r+1;
    }
    for(int i=1;i<=m;i++){
        printf("%d\n",res[i]);
    }
    return 0;
}

线段树代码
没有加快读会TLE掉
但是线段树功能强大
加深一下理解

#include <bits/stdc++.h>  //线段树TLE掉了
using namespace std;
const int N=1e6+5;
int a[N];
int las[N];
int pos[N];
int ans[N];
struct node{
    int l,r;
    int v;

}tree[N<<6];

struct node2{
    int l,r;
    int id;
    bool operator < (const node2& a)const{
        return r<a.r;
    }
}q[N];

void build(int l,int r,int flag)
{
    if(l==r){
        tree[flag].l=tree[flag].r=l;
        return ;
    }
    tree[flag].l=l;
    tree[flag].r=r;
    int mid=(l+r)/2;
    build(l,mid,flag*2);
    build(mid+1,r,flag*2+1);
}

void change(int flag,int pos,int type)
{
    if(tree[flag].r==tree[flag].l){
        tree[flag].v=type;
        return ;
    }
    int mid=(tree[flag].r+tree[flag].l)/2;
    if(pos<=mid){
        change(flag*2,pos,type);
    }else{
        change(flag*2+1,pos,type);
    }
    tree[flag].v=tree[flag*2].v+tree[flag*2+1].v;
}

int ask(int flag,int l,int r)
{
    if(tree[flag].l==l&&tree[flag].r==r){
        return tree[flag].v;
    }
    int mid=(tree[flag].l+tree[flag].r)/2;
    if(r<=mid){
        return ask(flag*2,l,r);
    }else if(l>mid){
        return ask(flag*2+1,l,r);
    }
    return ask(flag*2,l,mid)+ask(flag*2+1,mid+1,r);
}

int main()
{
    build(1,N-4,1);

    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
        las[i]=pos[a[i]];
        pos[a[i]]=i;
    }
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1);
    int las_r=1;
    for(int i=1;i<=m;i++){
        int l=q[i].l;
        int r=q[i].r;
        for(int i=las_r;i<=r;i++){
            if(las[i]!=0){
                change(1,las[i],0);
            }
            change(1,i,1);
        }
        ans[q[i].id]=ask(1,l,r);
        las_r=r+1;
    }
    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值