SPOJ DQUERY - D-query

SPOJ DQUERY - D-query

传送门
题意:给定区间内有多少个不同的数。
思路:离线树状数组||主席树
离线树状数组

先将询问按照r排序
然后依次将数放进数组
如果这个数出现过 将之前位置清零,当前位置+1
如果这个是没有出现过
就在当前位置+1

算法的正确性:因为是按照从左到右依次加入的,所以查询r区间里的l一定是正确的
所以只需要将r排序即可
代码如下

#include<cstdio>
#include<algorithm>
const int Maxn=30005;
const int Maxm=200005;
struct node{int l,r,id;}T[Maxm];
int c[Maxn],ans[Maxm],a[Maxn],w,n,m,mp[1000005];
bool cmp(node a,node b){return a.r<b.r;}
inline void update(int x,int v){while(x<=Maxn)c[x]+=v,x+=(x&-x);}
inline int sum(int x,int y)
{
    int s=0;for(;x!=0||y!=0;x-=(x&-x),y-=(y&-y))s+=c[x],s-=c[y];return s;
}
inline int read()
{
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')flag=1;
    else if(ch>='0'&&ch<='9')res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-'0';
    return flag?-res:res;
}
inline void Out(int a)
{
    if(a>9)Out(a/10);
    putchar(a%10+'0');
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    m=read();
    for(int i=0;i<m;i++)T[i].l=read(),T[i].r=read(),T[i].id=i;
    std::sort(T,T+m,cmp);
    w=1;
    for(int i=0;i<m;i++)
    {
        for(int j=w;j<=T[i].r;j++)
        {
            if(!mp[a[j]])update(j,1);
            else update(mp[a[j]],-1),update(j,1);
            mp[a[j]]=j;
        }
        ans[T[i].id]=sum(T[i].r,T[i].l-1);
        w=T[i].r+1;
    }
    for(int i=0;i<m;i++)Out(ans[i]),puts("");
    return 0;
}

主席树

i颗线段树维护的是位置1-i中有多少个不同的数
询问时只需要在第r颗线段树中询问[l,r]里有多少数就行了
更新线段树的方法 如果这个数i出现过
先定义一个临时的树
然后将这棵树的信息更改为第i-1颗树的信息并且将之前这个数i'从树中删除
然后用这颗临时的树去更新第i个数
如果这个数没有出现过 就直接用第i-1颗树去更新第i颗树

算法的正确性:因为每棵树维护的是[1,i]中不同数的个数,所以在树中询问[l,r]得到的一定是正确的。
代码如下

#include<cstdio>
const int Maxn=30005;
struct node{int l,r,tot;}T[Maxn*20];
int a[Maxn],n,m,l,r,mp[1000006],cnt,rt[Maxn],ans,tmp;
void update(int l,int r,int &x,int y,int k,int t)
{
    T[++cnt]=T[y];
    x=cnt;
    if(l==r){T[x].tot=T[y].tot+t;return;}
    int mid=(l+r)>>1;
    if(k<=mid)update(l,mid,T[x].l,T[y].l,k,t);
    else update(mid+1,r,T[x].r,T[y].r,k,t);
    T[x].tot=T[T[x].l].tot+T[T[x].r].tot;
}
void query(int l,int r,int ll,int rr,int x)
{
    if(ll<=l&&r<=rr){ans+=T[x].tot;return;}
    int mid=(l+r)>>1;
    if(ll<=mid)query(l,mid,ll,rr,T[x].l);
    if(rr>mid)query(mid+1,r,ll,rr,T[x].r);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(mp[a[i]])
        {
            update(1,n,tmp,rt[i-1],mp[a[i]],-1);
            update(1,n,rt[i],tmp,i,1);
        }
        else update(1,n,rt[i],rt[i-1],i,1);
        mp[a[i]]=i;
    }
    scanf("%d",&m);
    while(m--)
    {
        ans=0;
        scanf("%d%d",&l,&r);
        query(1,n,l,r,rt[r]);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值