hdu5919Sequence II

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5919

题意:给定n个数,q个询问,每次询问给定一组l,r,要求在线。求数组中l~r中每个数第一次出现的位置的中位数。

分析:我们可以求出每个数前一次出现的位置为pre[i],那么对于区间[l,r]中我们只需要求出右多少个pre[i]<l然后在这些数中求中位数,我们可以先求出总共有g个不同的数,然后二分判到第(g+1)/2个数在哪。这样做法是O(qlogn^2)的,在hdu上被卡。然后我们想办法能否在O(qlogn)内求解,我们会发现导致需要二分求解的原因是因为我们在当前点的主席树里存了多余的前缀点的位置,那么我们考虑逆序,root[i]从root[i+1]继承而来,而且如果a[i]有后继的话我们先删掉那个后继的位置,然后在插入当前位置,这样我们就能在当前点的权值线段树中只保存向后的每一个数第一次出现的位置。详见代码。O(qlogn)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=2e5+10;
const int M=5e4+10;
const int mod=1000000007;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=1000000010;
const ll MAX=1ll<<55;
const double eps=1e-5;
const double inf=~0u>>1;
const double pi=acos(-1.0);
typedef double db;
typedef unsigned int uint;
typedef unsigned long long ull;
int a[N],w[N],ans[N];
int siz,root[N],ls[36*N],rs[36*N],sum[36*N];
void update(int x,int &y,int l,int r,int w,int z) {
    y=++siz;sum[y]=sum[x]+z;
    ls[y]=ls[x];rs[y]=rs[x];
    if (l==r) return ;
    int mid=(l+r)>>1;
    if (w<=mid) update(ls[x],ls[y],l,mid,w,z);
    else update(rs[x],rs[y],mid+1,r,w,z);
}
int query(int x,int l,int r,int L,int R) {
    if (l==L&&r==R) return sum[x];
    int mid=(l+r)>>1;
    if (R<=mid) return query(ls[x],l,mid,L,R);
    else if (L>mid) return query(rs[x],mid+1,r,L,R);
        else return query(ls[x],l,mid,L,mid)+query(rs[x],mid+1,r,mid+1,R);
}
int find_k(int x,int l,int r,int k) {
    if (l==r) return l;
    int mid=(l+r)>>1;
    if (sum[ls[x]]>=k) return find_k(ls[x],l,mid,k);
    else return find_k(rs[x],mid+1,r,k-sum[ls[x]]);
}
int main()
{
    int i,g,n,m,x,ca,l,r,T;
    scanf("%d", &T);
    for (ca=1;ca<=T;ca++) {
        scanf("%d%d", &n, &m);
        for (i=1;i<=n;i++) scanf("%d", &a[i]);
        ans[0]=siz=root[n+1]=0;
        memset(w,0,sizeof(w));
        for (i=n;i;i--) {
            update(root[i+1],x,1,n,i,1);
            if (w[a[i]]) update(x,root[i],1,n,w[a[i]],-1);
            else root[i]=x;
            w[a[i]]=i;
        }
        for (i=1;i<=m;i++) {
            scanf("%d%d", &l, &r);
            l=(ans[i-1]+l)%n+1;
            r=(ans[i-1]+r)%n+1;
            if (l>r) swap(l,r);
            g=query(root[l],1,n,l,r);
            ans[i]=find_k(root[l],1,n,g+1>>1);
        }
        printf("Case #%d:", ca);
        for (i=1;i<=m;i++) printf(" %d", ans[i]);
        printf("\n");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值