HDU 5919 Sequence II 主席树

题目链接:

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

Sequence II

Time Limit: 9000/4500 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
#### 问题描述 > Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,⋯,an There are m queries. > > In the i-th query, you are given two integers li and ri. Consider the subsequence ali,ali+1,ali+2,⋯,ari. > > We can denote the positions(the positions according to the original sequence) where an integer appears first in this subsequence as p(i)1,p(i)2,⋯,p(i)ki (in ascending order, i.e.,p(i)1 > Note that ki is the number of different integers in this subsequence. You should output p(i)⌈ki2⌉for the i-th query. #### 输入 > In the first line of input, there is an integer T (T≤2) denoting the number of test cases. > > Each test case starts with two integers n (n≤2×105) and m (m≤2×105). There are n integers in the next line, which indicate the integers in the sequence(i.e., a1,a2,⋯,an,0≤ai≤2×105). > > There are two integers li and ri in the following m lines. > > However, Mr. Frog thought that this problem was too young too simple so he became angry. He modified each query to l‘i,r‘i(1≤l‘i≤n,1≤r‘i≤n). As a result, the problem became more exciting. > > We can denote the answers as ans1,ans2,⋯,ansm. Note that for each test case ans0=0. > > You can get the correct input li,ri from what you read (we denote them as l‘i,r‘i)by the following formula: > li=min{(l‘i+ansi−1) mod n+1,(r‘i+ansi−1) mod n+1} > > ri=max{(l‘i+ansi−1) mod n+1,(r‘i+ansi−1) mod n+1} #### 输出 > You should output one single line for each test case. > > For each test case, output one line “Case #x: p1,p2,⋯,pm”, where x is the case number (starting from 1) and p1,p2,⋯,pm is the answer. ####样例输入 > 2 > 5 2 > 3 3 1 5 4 > 2 2 > 4 4 > 5 2 > 2 5 2 1 2 > 2 3 > 2 4

样例输出

Case #1: 3 3
Case #2: 3 1

题意

求区间[l,r]的所有不重复的数(重复的以第一次出现的位置为准)位于中间(位置在中间,不是值)那个数所在的位置。
题目强制在线

题解

用主席树,倒过来建树,从第n个位置建到第1个位置,对于重复的数边做边删,保证只留在最左边的那个,然后对于查询[l,r],我们去查rt[l]那棵树的l,r区间,得到不重复的数有cnt个,然后用求区间第k大的办法去求第(cnt+1)/2大的数。

还有一种思路,每个数存它前驱的位置,然后用主席树求区间[l,r]中比l小的数的个数,从而可以求区间[l,r]内不同的数的个数,然后二分t,去找中间的位置, 这样的复杂度是nlogn+q*logn*logn,会卡时间,反正我是t了。

代码

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<sstream>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define mid (l+(r-l)/2)
#define lson(o) (tre[(o)].ls)
#define rson(o) (tre[(o)].rs)
#define sumv(o) (tre[(o)].sum)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf

typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;

const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);

//start----------------------------------------------------------------------

const int maxn=201010;

///nlogn空间复杂度
///注意:由于有增加和删除两个操作!所以空间是2n*log(2n)
struct Tre {
    int ls,rs,sum;
    Tre() {
        ls=rs=sum=0;
    }
} tre[maxn*50];

int n,m;
int rt[maxn],tot;

int _v,_p;
void update(int &o,int l,int r) {
    tre[++tot]=tre[o],o=tot;
    if(l==r) {
        sumv(o)+=_v;
    } else {
        if(_p<=mid) update(lson(o),l,mid);
        else update(rson(o),mid+1,r);
        sumv(o)=sumv(lson(o))+sumv(rson(o));
    }
}

///区间内不同的数有几个
int _res,ql,qr;
void query(int o,int l,int r) {
    if(ql<=l&&r<=qr) {
        _res+=sumv(o);
    } else {
        if(ql<=mid) query(lson(o),l,mid);
        if(qr>mid) query(rson(o),mid+1,r);
    }
}

///区间第k大
int _res2;
void query2(int o,int l,int r,int k) {
    if(l==r) {
        _res2=l;
    } else {
        if(k<=sumv(lson(o))) query2(lson(o),l,mid,k);
        else query2(rson(o),mid+1,r,k-sumv(lson(o)));
    }
}

int arr[maxn],mp[maxn];

///0是个超级节点
void init() {
    rt[n+1]=tot=0;
}

int main() {
    int tc,kase=0;
    scf("%d",&tc);
    while(tc--) {
        scf("%d%d",&n,&m);
        init();
        for(int i=1; i<=n; i++) {
            scf("%d",&arr[i]);
        }

        ///主席树
        for(int i=1; i<=n; i++) mp[i]=n+1;
        for(int i=n; i>=1; i--) {
            rt[i]=rt[i+1];
            _v=1,_p=i;
            update(rt[i],1,n);
            if(mp[arr[i]]<=n) {
                _v=-1,_p=mp[arr[i]];
                update(rt[i],1,n);

            }
            mp[arr[i]]=i;
        }

        int ans=0;
        VI lis;
        while(m--) {
            int ll,rr;
            scf("%d%d",&ll,&rr);
            int t1=(ll+ans)%n+1,t2=(rr+ans)%n+1;
            int l=min(t1,t2),r=max(t1,t2);

            ///求区间内不同的数有几个
            ql=l,qr=r,_res=0;
            query(rt[l],1,n);
            int k=(_res+1)/2;


            ///求区间第k大
            query2(rt[l],1,n,k);

            ans=_res2;
            lis.pb(ans);
        }
        prf("Case #%d:",++kase);
        rep(i,0,lis.sz()) {
            prf(" %d",lis[i]);
        }
        putchar('
');
    }
    return 0;
}

//end-----------------------------------------------------------------------
原文地址:https://www.cnblogs.com/fenice/p/5933231.html