您的位置:首页 > 其它

HDU5654(多种解法小结)

2016-03-31 00:33 211 查看

前言

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

分析

离散化以后就变成了一个经典题。

经典问题:区间[l,r][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_back

const 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;
}


主席树

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

#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  second

typedef 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  second

const 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: