|
题目链接: http://poj.org/problem?id=2892
/**//* 题意: 给出M(M <= 50000)组操作,每组操作的形式如下: 1 D x: 将第x个村庄摧毁 2 Q x: 询问和第x个村庄直接或者间接连接的村庄数目 3 R: 修复上一个被摧毁的村庄
解法: 线段树 或者 树状数组
思路: 线段树染色问题,和ZJU 2301 Color the Ball类似,也是寻找最长连 续区间。但是这题要简单的多,因为插入的时候是对点操作,不需要用到 lazy标记,线段树中可以保存如下信息:
enum eKind { EK_BLACK = 0, EK_WHITE = 1, };
int root, l, r; int lMax; // 包含左区间的连续空闲区间的最大值 int rMax; // 包含右区间的连续空闲区间的最大值 int mMax; // 当前结点管辖区间的最大值
首先来看下问题对应的操作,题目中有两种插入,一个是插入一个摧毁 的村庄,另一个是插入一个修复的村庄,其实原理是一样的。只不过这两种 类型更新lMax,rMax,mMax的时候稍有不同,每次插入到线段树元区间时, 根据插入的eKind类型填充mMax、lMax、rMax的信息,否则更新他们的结点 信息,然后递归左右儿子,继续插入操作,递归返回时我们用以下函数从左 右儿子中得到当前结点的信息: void UpdateBy(Tree* ls, Tree* rs); 之所以把它写成函数是因为这里的处理比较麻烦,很容易出错,并且需要 调用多次,这个函数的作用就是通过左右儿子的信息填充本身的信息。 信息一多,处理的时候就要极为小心,因为很容易出错。 lMax表示当前结点的包含左闭区间的最优解。 rMax表示当前结点的包含右闭区间的最优解。 mMax则是当前区间的最优解。 这样我们就可以通过传递性在儿子结点的属性都得知的情况下将父亲的值 计算出来,最后递归到根结点。具体的计算过程可以自己画棵树看一下, 然后是查询操作,查询的话首先来看递归出口,我们用两个引用来表示 当前查到的信息是否左连通以及是否右连通,那么如果到元区间的时候当前 点的mMax是0,那么直接左右连通标记都标记为false,否则为true。如果不 是元区间,则判断当前点在左儿子还是右儿子,如果在左儿子,那么递归左 右子,回归时根据返回的引用值判断当前是否右连通,如果右连通,那么将 右儿子的lMax值加到答案上来,再判断lMax是否等于右儿子的len,如果不 相等说明下次回归上去的时候必然不是右连通,标记置为false,对于右儿子 的情况做相同处理即可。回归到根结点就是最后的答案了。 这题还可以用树状数组来做,写起来也比较方便,还是利用成段求和来 统计当前点区间内的K大值,K值用二分枚举即可。 */ #include <iostream>
using namespace std;
#define maxn 50010
enum eKind { EK_BLACK = 0, EK_WHITE = 1, };
struct Tree { int root, l, r; int lMax, rMax, mMax;
void CoverBy(eKind ek); void UpdateBy(Tree* ls, Tree* rs);
int len() { return r - l + 1; } }T[4*maxn];
void Build(int p, int l, int r) { T[p].root = p; T[p].l = l; T[p].r = r; T[p].rMax = T[p].lMax = T[p].mMax = T[p].len(); if(l == r) return ; int mid = (l + r) >> 1; Build(p<<1, l, mid); Build(p<<1|1, mid+1, r); }
int MMax(int a, int b) { return a > b ? a : b; }
int MMax(int a, int b, int c, int d) { return MMax( MMax(a, b), MMax(c, d) ); }
void Tree::UpdateBy(Tree* ls, Tree* rs) { lMax = ls->lMax; if(lMax == ls->len()) lMax += rs->lMax; rMax = rs->rMax; if(rMax == rs->len()) rMax += ls->rMax; mMax = MMax(lMax, rMax); mMax = MMax(mMax, ls->mMax, rs->mMax, ls->rMax + rs->lMax); }
void Tree::CoverBy(eKind ek) { mMax = lMax = rMax = (ek==EK_WHITE) ? len() : 0; }
void Insert(int p, int pos, eKind ek) { if(pos > T[p].r || pos < T[p].l) return ; if(T[p].l == pos && pos == T[p].r) { T[p].CoverBy(ek); return ; } Insert(p<<1, pos, ek); Insert(p<<1|1, pos, ek);
T[p].UpdateBy(&T[p<<1], &T[p<<1|1]); }
int Query(int p, int pos, bool& lc, bool& rc) { if(T[p].l == pos && pos == T[p].r) { lc = rc = T[p].mMax; return T[p].mMax; }
int x; if(pos <= T[p<<1].r) { x = Query(p<<1, pos, lc, rc); if(rc) { x += T[p<<1|1].lMax; if(T[p<<1|1].lMax < T[p<<1|1].len()) rc = false; } }else { x = Query(p<<1|1, pos, lc, rc); if(lc) { x += T[p<<1].rMax; if(T[p<<1].rMax < T[p<<1].len()) lc = false; } } return x; }
int n, m; int stack[maxn], top; int main() { char str[5]; int x; while(scanf("%d %d", &n, &m) != EOF) { Build(1, 1, n); top = 0; while(m--) { scanf("%s", str); if(str[0] == 'D') { scanf("%d", &stack[top]); Insert(1, stack[top], EK_BLACK); top ++; }else if(str[0] == 'R') { if(top) { top --; Insert(1, stack[top], EK_WHITE); } }else { scanf("%d", &x); // 左连通、右连通 bool lc, rc; printf("%d\n", Query(1, x, lc, rc)); } } } return 0; }
|