HDU5654(多种解法小结)

来源:互联网 发布:淘宝网男士保暖上衣 编辑:程序博客网 时间:2024/06/07 15:33

前言

题目不难,但是以前写这类题目总是感觉有点麻烦,现在来稍微小结一下。

分析

离散化以后就变成了一个经典题。
经典问题:区间[l,r]中共有多少个不同的数。

这道题比较常见的写法:离线线段树
当然主席树莫队都可以写。

离线线段树

类似最长上升子序列的想法,从左向右更新,每个数在线段树中,只存放在最右边的位置,即每遇到一个数,先删除这个数的前一个位置,然后在现在的位置添加。这样就可以保证每一个数在线段树中只出现一次了。此时问题就变简单了:
1. 预处理前驱
2. 线段树的查询更新(树状数组)

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef vector <int>    VI;typedef pair <int,int>  PII;typedef pair <pair<int,int>,int>  PIII;#define FOR(i,x,y)  for(int i = x;i < y;++ i)#define IFOR(i,x,y) for(int i = x;i > y;-- i)#define fi  first#define se  second#define mp  make_pair#define pb  push_backconst int maxn = 200010;int n,q,c[maxn];int lowbit(int x)   {return x&(-x);}void update(int x,int val){    while(x <= n){        c[x] += val;        x += lowbit(x);    }}int query(int x){    int ans = 0;    while(x){        ans += c[x];        x -= lowbit(x);    }    return ans;}int sz,a[maxn],p[maxn],pl[maxn],ans[maxn];PIII b[maxn],cmd[maxn];void work(){    FOR(i,0,n)  c[i] = 0;    FOR(i,1,sz+1)   pl[i] = -1;     int cur = 1;    FOR(i,0,q){        if(cmd[i].fi.fi < cmd[i].fi.se) {ans[cmd[i].se] = 0;continue;}        for(;cur <= cmd[i].fi.fi;cur ++){            if(!p[cur])    continue;            if(pl[p[cur]] != -1){                update(pl[p[cur]],-1);            }               update(cur,1);            pl[p[cur]] = cur;        }        //printf("%d %d\n",query(cmd[i].fi.se-1),query(cmd[i].fi.fi));        ans[cmd[i].se] = query(cmd[i].fi.fi)-query(cmd[i].fi.se-1);    }    FOR(i,0,q)  printf("%d\n",ans[i]);}int main(){    int T;  scanf("%d",&T);    while(T--){        scanf("%d",&n);        FOR(i,0,n)  scanf("%d",&a[i]);        sz = 0;        FOR(i,1,n-1) if(a[i-1] <= a[i] && a[i] <= a[i+1]){            b[sz++] = mp(mp(a[i-1],a[i]),a[i+1]);        }        sort(b,b+sz);        sz = unique(b,b+sz)-b;        FOR(i,1,n-1){            if(a[i-1] <= a[i] && a[i] <= a[i+1]){                p[i] = lower_bound(b,b+sz,mp(mp(a[i-1],a[i]),a[i+1]))-b+1;            }            else    p[i] = 0;        }        scanf("%d",&q); int l,r;        FOR(i,0,q)  {scanf("%d%d",&l,&r);cmd[i] = mp(mp(r-2,l),i);}        sort(cmd,cmd+q);        //FOR(i,0,q)  printf("%d %d %d\n",cmd[i].fi.fi,cmd[i].fi.se,cmd[i].se);        work();    }    return 0;}

主席树

写主席树就是无脑题了,不过这道题我的写法一直过不了,总是提示MLE,好尴尬。

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef vector <int>    VI;typedef pair <int,int>  PII;#define FOR(i,x,y)  for(int i = x;i < y;++ i)#define IFOR(i,x,y) for(int i = x;i > y;-- i)#define pb  push_back#define mp  make_pair#define fi  first#define se  secondtypedef pair <PII,int>  PIII;const int maxn = 200010;int a[maxn],c[maxn],n,q,sz;vector <PIII >   b;map <PIII,int>  mat;void input(){    scanf("%d",&n); b.clear();    FOR(i,0,n)  scanf("%d",&a[i]);    FOR(i,2,n){        if(a[i-2] <= a[i-1] && a[i-1] <= a[i]){            b.pb(mp(mp(a[i-2],a[i-1]),a[i]));        }    }    sort(b.begin(),b.end());    b.erase(unique(b.begin(),b.end()),b.end());    sz = b.size();      FOR(i,0,sz) mat[b[i]] = (i+1);    FOR(i,2,n){        if(a[i-2] <= a[i-1] && a[i-1] <= a[i]){            c[i-1] = mat[mp(mp(a[i-2],a[i-1]),a[i])];         }        else    c[i-1] = 0;    }}int rt[maxn],tot;struct Tree{    int ls,rs,sum;}tree[maxn*20];int build(int l,int r){    int o = tot++;    tree[o].sum = 0;    if(l == r)  return o;    int mid = (l+r)>>1;    tree[o].ls = build(l,mid);    tree[o].rs = build(mid+1,r);    return o;}int update(int x,int l,int r,int lt,int val){    int o = tot++;    tree[o]  = tree[lt];    tree[o].sum += val;    if(l == r)  return o;    int mid = (l+r)>>1;    if(x <= mid)    tree[o].ls = update(x,l,mid,tree[lt].ls,val);    else    tree[o].rs = update(x,mid+1,r,tree[lt].rs,val);    return o;}void debug(int o,int l,int r){    if(l == r)  {printf("%d:%d ",l,tree[o].sum);return;}    int mid = (l+r)>>1;    debug(tree[o].ls,l,mid);    debug(tree[o].rs,mid+1,r);}void work(){    tot = 0;    rt[0] = build(1,sz);    FOR(i,2,n){        if(c[i-1])  rt[i-1] = update(c[i-1],1,sz,rt[i-2],1);        else    rt[i-1] = update(sz,1,sz,rt[i-2],0);    }    scanf("%d",&q);    FOR(i,0,q){        int l,r;    scanf("%d%d",&l,&r);        r -= 2;        if(r < l)   printf("0\n");        else    printf("%d\n",tree[rt[r]].sum - tree[rt[l-1]].sum);    }}int main(){    int T;  scanf("%d",&T);    while(T--){        input();        work();    }    return 0;}

莫队

感觉这道题在莫队上面也是比较经典的题。暴力添加,删除的时候如何维护最终答案。这个时候需要当前数的前驱和后驱,如果左边添加(删除)的话,看看这个数的后驱有没有超过当前区间(或者没有),如果右边添加(删除)的话,看看这个数的前驱有没有超过当前区间(或者没有)。

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef vector <int>    VI;typedef pair <pair<int,int>,int>    PIII;#define FOR(i,x,y)  for(int i = x;i < y;++ i)#define IFOR(i,x,y) for(int i = x;i > y;-- i)#define mp  make_pair#define pb  push_back#define fi  first#define se  secondconst int maxn = 200200;const int M = (int)sqrt(maxn*1.0)+1;int a[maxn],c[maxn],n,q,sz;int pl[maxn],pr[maxn],p[maxn];int ans[maxn];PIII b[maxn];struct Commend{    int id,l,r;    Commend()   {}    Commend(int x,int y,int z) : id(x),l(y),r(z)    {}    bool operator < (const Commend &rhs) const{        if(l/M == rhs.l/M)  return r < rhs.r;        return l/M < rhs.l/M;    }}cmd[maxn];void input(){    scanf("%d",&n);    FOR(i,0,n)  scanf("%d",&a[i]);    sz = 0;    FOR(i,1,n-1){        if(a[i-1] <= a[i] && a[i] <= a[i+1]){            b[sz++] = mp(mp(a[i-1],a[i]),a[i+1]);        }    }    sort(b,b+sz);    sz = unique(b,b+sz)-b;    FOR(i,1,n-1)    pl[i] = pr[i] = -1;    FOR(i,1,sz+1)   p[i] = -1;    FOR(i,1,n-1){        if(a[i-1] <= a[i] && a[i] <= a[i+1]){            PIII tem = mp(mp(a[i-1],a[i]),a[i+1]);            int id = lower_bound(b,b+sz,tem)-b+1;            pl[i] = p[id];            if(p[id] != -1) pr[p[id]] = i;            p[id] = i;            c[i] = id;        }        else    c[i] = 0;    }    scanf("%d",&q);    FOR(i,0,q){        int l,r;        scanf("%d%d",&l,&r);        r -= 2;        cmd[i] = Commend(i,l,r);    }    sort(cmd,cmd+q);}void work(){    int L = 1,R = 0;    int res = 0;    FOR(i,0,q){        if(cmd[i].r < cmd[i].l) {ans[cmd[i].id] = 0;continue;}        while(R < cmd[i].r){            R ++;            if(c[R] && (pl[R] == -1 || pl[R] < L))    res ++;        }        while(R > cmd[i].r){            if(c[R] && (pl[R] == -1 || pl[R] < L))    res --;            R --;        }        while(L < cmd[i].l){            if(c[L] && (pr[L] == -1 || pr[L] > R))    res --;            L ++;        }        while(L > cmd[i].l){            L --;            if(c[L] && (pr[L] == -1 || pr[L] > R))    res ++;        }        ans[cmd[i].id] = res;    }    FOR(i,0,q){        printf("%d\n",ans[i]);    }}int main(){    //freopen("test.in","r",stdin);    int T;  scanf("%d",&T);    while(T--){        input();        work();    }    return 0;}
0 0
原创粉丝点击