摘自解题报告:
题意为询问一段区间里的数能组成多少段连续的数。先考虑从左往右一个数一个数添加,考虑当前添加了i - 1个数的答案是x,那么添加完i个数后的答案是多少?可以看出,是根据a[i]-1和a[i]+1是否已经添加而定的,如果a[i]-1或者a[i]+1已经添加一个,则段数不变,如果都没添加则段数加1,如果都添加了则段数减1。设v[i]为加入第i个数后的改变量,那么加到第x数时的段数就是sum{v[i]} (1<=i<=x}。仔细想想,若删除某个数,那么这个数两端的数的改变量也会跟着改变,这样一段区间的数构成的段数就还是他们的v值的和。将询问离线处理,按左端点排序后扫描一遍,左边删除,右边插入,查询就是求区间和。
自己比赛看的是这道题,但是想了很久都没有想到处理询问的姿势.............,too young...
怎么最近喜欢出离线询问的线段树了??..............................
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 111111
#define INF 0x7FFFFFFF
#define REP(i,s,t) for(int i=(s);i<=(t);++i)
#define mem(a,b) memset(a,b,sizeof(a))
#define mp(a,b) make_pair(a,b)
#define L(x) x<<1
#define R(x) x<<1|1
# define eps 1e-5
//#pragma comment(linker, "/STACK:36777216") ///传说中的外挂
using namespace std;
struct node {
int l,r,mid,sum,cover;
} tree[MAX*4];
int n,m;
int a[MAX],pos[MAX],in[MAX],vis[MAX],va[MAX],ans[MAX];
struct Node {
int l,r;
int id;
} qes[MAX];
void init() {
memset(in,0,sizeof(in));
memset(pos,0,sizeof(pos));
memset(vis,0,sizeof(vis));
memset(va,0,sizeof(va));
}
void up(int num) {
tree[num].sum = tree[L(num)].sum + tree[R(num)].sum;
}
void build(int l,int r,int num) {
tree[num].l = l;
tree[num].r = r;
tree[num].mid = (l+r) >> 1;
tree[num].cover = 0;
tree[num].sum = 0;
if(l == r) {
tree[num].sum = va[l];
return ;
}
build(l,tree[num].mid,L(num));
build(tree[num].mid + 1,r,R(num));
up(num);
}
void update(int l,int x,int color) {
if(tree[x].l == tree[x].r) {
tree[x].sum += color;
return ;
}
if(l > tree[x].mid) update(l,R(x),color);
else update(l,L(x),color);
up(x);
}
int query(int l,int r,int num) {
if(l == tree[num].l && tree[num].r == r) {
return tree[num].sum;
}
if(r <= tree[num].mid) {
return query(l,r,L(num));
} else if(l > tree[num].mid) {
return query(l,r,R(num));
} else {
return query(l,tree[num].mid,L(num)) + query(tree[num].mid+1,r,R(num));
}
}
void test(int n) {
for(int i=1; i<=3*n; i++) {
printf("l:%d r:%d cover:%d sum: %d\n",tree[i].l,tree[i].r,tree[i].cover,tree[i].sum);
}
}
bool cmp(Node a,Node b) {
return a.l < b.l;
}
int main() {
int T;
cin >> T;
while(T --) {
scanf("%d%d",&n,&m);
init();
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
pos[a[i]] = i;
}
//从左向右一个个插入
for(int i=1; i<=n; i++) {
int cnt = 0;
if(in[a[i] - 1] == 1) cnt ++;
if(in[a[i] + 1] == 1) cnt ++;
if(cnt == 0) {
va[i] ++;
}
if(cnt == 2) {
va[i] -- ;
}
in[a[i]] = 1;
}
build(1,n,1);
for(int i=1; i<=m; i++) {
scanf("%d%d",&qes[i].l,&qes[i].r);
qes[i].id = i;
}
sort(qes+1,qes+1+m,cmp);
int order = 1;
for(int i=1; i<=m; i++) {
while(qes[i].l > order) {
if(pos[a[order] + 1] > order && a[order] + 1 <= n) {
update(pos[a[order] + 1],1,1);
}
if(pos[a[order] - 1] > order && a[order] - 1 >= 1) {
update(pos[a[order] - 1],1,1);
}
order ++;
}
ans[qes[i].id] = query(qes[i].l,qes[i].r,1);
}
for(int i=1; i<=m; i++) {
printf("%d\n",ans[i]);
}
}
return 0;
}
博客内容讲述了如何解决HDU 4638 Group的问题,该问题涉及到计算区间内连续数段的数量。通过分析,博主发现可以利用线段树结合离线询问的方法,对每个数的加入和删除影响进行计算。博主在比赛中遇到困难,感叹近期离线询问在竞赛中频繁出现。
234

被折叠的 条评论
为什么被折叠?



