Problem
【ZJOI2007】Hide捉迷藏
TimeLimit:40Sec
MemoryLimit:256MB
Description
捉迷藏Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N−1条双向走廊组成,这N−1条走廊的分布使得任意两个屋子都互相可达。
游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。
我们将以如下形式定义每一种操作:
- C(hange)i :改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。
- G(ame) :开始一次游戏,查询最远的两个关灯房间的距离。
第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3⋯N的整数。接下来N−1行每行两个整数a,b,表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。
Output
对于每一个操作G(ame),输出一个非负整数,表示最远的两个关灯房间的距离。
若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出−1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 8 1 2 2 3 3 4 3 5 3 6 6 7 6 8 7 G C 1 G C 2 G C 1 G
|
Sample Output
Hint
对于100%的数据,N≤105,M≤5×105。
标签:括号序列
线段树
Solution
不会写动态点分,受小岛姐的启发写了线段树,虽然有7个标记,但推出公式还是比较好写的。
括号序列
对于一棵树,我们可以将其DFS序稍加优化,得到一个能记录每个点的子树结构的序列,即括号序列。
用左括号(表示进入某点的子树,用右括号)表示走出某点的子树。
对于此树,括号序列为(A(B)(C(D)(E))),可以看出括号序列有一个有用的性质,即仍两点之间的序列可以表示从前面的点走到后面的点的走法。例如对于点B和E,其中间的序列为)(()(,如果去掉中间匹配的括号则变为)((,将)定义为向上走,将(定义为向下走,那么可以看出B向上走两步,再向下走两步即可到E。那么如果维护此题中的树的括号序列,那么只需要知道任意两个黑点间的括号序列消除配对后的长度的最大值后即可得到答案。
线段树维护
对于一个序列的区间,可以用二元组(a,b)表示其消去配对后的序列,即有a个右括号和b个左括号。
对于区间S1(a1,b1)和S2(a2,b2),它们合并起来的区间是S(a,b),那么
- 当b1>a2时,S1的左括号和S2的右括号消去后一定会剩下b1−a2个左括号,因此a=a1−b1+a2,b=b2
- 当b1≤a2时,S1的左括号和S2的右括号消去后一定会剩下a2−b1个右括号,因此a=a1,b=−a2+b1+b2
那么易得到几个推论:
- a+b=a1+a2+∣b1−a2∣
- a−b=a1+a2−b1−b2
- b−a=−a1−a2+b1+b2
我们需要维护每个区间中两黑点间括号序列长度的最大值,那么我们还需要维护另外几个信息(有点像区间最大字段和):
- lp=max{a+b∣S’(a,b)是S的一个前缀,且一个黑点接在S′之后}
- lm=max{b−a∣S’(a,b)是S的一个前缀,且一个黑点接在S′之后}
- rp=max{a+b∣S′(a,b)是S的一个后缀,且S′接在一个黑点后}
- rm=max{a−b∣S′(a,b)是S的一个后缀,且S′接在一个黑点后}
对于一个区间S,其两个子区间S1,S2的七个值分别为a1,b1,lp1,lm1,rp1,rm1,dis1和a2,b2,lp2,lm2,rp2,rm2,dis2,那么该区间的dis一定是以下四个值的最大值:
- dis1
- dis2
- rp1+lm2
- rm1+lp2
而对于维护的4个信息,观察发现可以这样计算:
- lp=max{lp1,a1−b1+lp2,a1+b1+lm2}
- lm=max{lm1,−a1+b1+lm2}
- rp=max{rp2,−a2+b2+rp1,a2+b2+rm1}
- rm=max{rm2,a2−b2+rm1}
这样以后我们就可以用线段树维护这七个标记了。
除了update有点长以外,其他都和裸线段树一样。
以后找时间把动态点分学一学吧。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| #include <bits/stdc++.h> #define MAX_N 100000 #define INF 0x3f3f3f3f #define mid ((s+t)>>1) using namespace std; template <class T> inline void read(T &x) { x = 0; int c = getchar(), f = 1; for (; !isdigit(c); c = getchar()) if (c == 45) f = -1; for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0'); } vector <int> G[MAX_N+5]; int n, cnt, col[MAX_N+5]; int ind, seq[(MAX_N<<2)+5], dfn[MAX_N+5]; struct node {int a, b, lp, lm, rp, rm, dis;} tr[(MAX_N<<4)+5]; void addedge(int u, int v) {G[u].push_back(v), G[v].push_back(u);} void DFS(int u, int f) { seq[++ind] = -1, seq[++ind] = u, dfn[u] = ind; for (int i = 0, v; i < (int)G[u].size(); i++) if ((v = G[u][i]) ^ f) DFS(v, u); seq[++ind] = -2; } void init(int v, int p) { tr[v].a = tr[v].b = 0, tr[v].dis = -INF; if (seq[p] == -1) tr[v].b = 1; if (seq[p] == -2) tr[v].a = 1; if (seq[p] > 0 && col[seq[p]]) tr[v].lp = tr[v].lm = tr[v].rp = tr[v].rm = 0; else tr[v].lp = tr[v].lm = tr[v].rp = tr[v].rm = -INF; } void update(int v, int s, int t) { int a = tr[v<<1].a, b = tr[v<<1].b; int c = tr[v<<1|1].a, d = tr[v<<1|1].b; tr[v].a = b < c ? a-b+c : a; tr[v].b = b < c ? d : b-c+d; tr[v].lp = tr[v<<1].lp, tr[v].rp = tr[v<<1|1].rp; tr[v].lm = tr[v<<1].lm, tr[v].rm = tr[v<<1|1].rm; tr[v].lp = max(tr[v].lp, tr[v<<1|1].lp+a-b); tr[v].lp = max(tr[v].lp, tr[v<<1|1].lm+a+b); tr[v].lm = max(tr[v].lm, tr[v<<1|1].lm-a+b); tr[v].rp = max(tr[v].rp, tr[v<<1].rp-c+d); tr[v].rp = max(tr[v].rp, tr[v<<1].rm+c+d); tr[v].rm = max(tr[v].rm, tr[v<<1].rm+c-d); tr[v].dis = max(tr[v<<1].dis, tr[v<<1|1].dis); tr[v].dis = max(tr[v].dis, tr[v<<1|1].lm+tr[v<<1].rp); tr[v].dis = max(tr[v].dis, tr[v<<1|1].lp+tr[v<<1].rm); } void build(int v, int s, int t) { if (s == t) {init(v, s); return;} build(v<<1, s, mid), build(v<<1|1, mid+1, t); update(v, s, t); } void modify(int v, int s, int t, int p) { if (s == t) {init(v, s); return;} if (p <= mid) modify(v<<1, s, mid, p); else modify(v<<1|1, mid+1, t, p); update(v, s, t); } int main() { read(n), cnt = n; for (int i = 1; i <= n; i++) col[i] = 1; for (int i = 1, u, v; i < n; i++) read(u), read(v), addedge(u, v); int T; read(T), DFS(1, 0), build(1, 1, ind); while (T--) { char opt[2]; scanf("%s", opt); if (opt[0] == 'C') { int x; read(x); cnt += col[x] ? -1 : 1, col[x] ^= 1; modify(1, 1, ind, dfn[x]); } if (opt[0] == 'G') { if (!cnt) puts("-1"); else if (cnt == 1) puts("0"); else printf("%d\n", tr[1].dis); } } return 0; }
|