BZOJ 2243 树链剖分
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2243
题意:中文题目
思路:树链剖分。首先考虑求区间颜色段数的问题, 我们可以用线段树维护:区间左右端点(st,ed),区间颜色段数(val),懒惰标记(lazy:是否整个区间被染成同一种颜色),区间左右端点的颜色(lcolor,rcolor),然后在查询的时候如果当前区间的左子树的右端点的颜色等于右子树的左端点的颜色,那么查询答案要减一。由于树链剖分在查询时是有可能两端的分链一起向上爬的。所以我们在查询时需要记录两端上一次查询结果的deep小的那个点的颜色。如果当前查询的链和上一次查询的交接处位置颜色一致那么答案就要减一。
注意: 颜色可能为0
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<stdio.h> #include<queue> #include<vector> #include<stack> #include<map> #include<set> #include<time.h> #include<cmath> #include<sstream> #include<assert.h> using namespace std; #define L(x) x<<1 #define R(x) x<<1|1 typedef long long int LL; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3fLL; const int MAXN = 1e5 + 10; int head[MAXN], tot, cnt,value[MAXN]; struct Edge{ int to, next; }Edges[MAXN * 2]; void add(int u, int v){ Edges[tot].to = v; Edges[tot].next = head[u]; head[u] = tot++; } int id[MAXN], son[MAXN], deep[MAXN], size[MAXN], fa[MAXN], reid[MAXN], top[MAXN]; void Init(){ tot = 0; cnt = 0; memset(head, -1, sizeof(head)); memset(son, -1, sizeof(son)); } void DFS1(int u, int p,int dep){ fa[u] = p; size[u] = 1; deep[u] = dep; for (int i = head[u]; i != -1; i = Edges[i].next){ if (Edges[i].to != p){ DFS1(Edges[i].to, u,dep+1); size[u] += size[Edges[i].to]; if (son[u] == -1 || size[Edges[i].to] > size[son[u]]){ son[u] = Edges[i].to; } } } } void DFS2(int u, int tp){ id[u] = ++cnt; reid[id[u]] = u; top[u] = tp; if (son[u] == -1){ return; } DFS2(son[u], tp); for (int i = head[u]; i != -1; i = Edges[i].next){ if (son[u] != Edges[i].to&&Edges[i].to != fa[u]){ DFS2(Edges[i].to, Edges[i].to); } } } struct Node{ int st, ed; int lazy,val, lcolor,rcolor; //懒惰标记,颜色段数,区间左右端点的颜色 }Seg[MAXN * 4]; void pushUp(int k){ Seg[k].lcolor = Seg[L(k)].lcolor; Seg[k].rcolor = Seg[R(k)].rcolor; Seg[k].val = Seg[L(k)].val + Seg[R(k)].val; if (Seg[L(k)].rcolor == Seg[R(k)].lcolor){ Seg[k].val--; } } void pushDown(int k){ //颜色可能等于0 if (Seg[k].lazy!=-1){ Seg[L(k)].val = 1; Seg[L(k)].lcolor = Seg[L(k)].rcolor = Seg[L(k)].lazy = Seg[k].lazy; Seg[R(k)].val = 1; Seg[R(k)].lcolor = Seg[R(k)].rcolor = Seg[R(k)].lazy = Seg[k].lazy; Seg[k].lazy = -1; } } void Build(int l, int r, int k){ Seg[k].st = l; Seg[k].ed = r; Seg[k].lazy = -1; if (l == r){ Seg[k].val = 1; Seg[k].lcolor = Seg[k].rcolor = value[reid[l]]; return; } int mid = (l + r) / 2; Build(l, mid, L(k)); Build(mid + 1, r, R(k)); pushUp(k); } void Modify(int l,int r,int val,int k){ if (Seg[k].st == l&&Seg[k].ed == r){ Seg[k].lazy = Seg[k].lcolor = Seg[k].rcolor = val; Seg[k].val = 1; return; } pushDown(k); if (r <= Seg[L(k)].ed){ Modify(l, r, val, L(k)); } else if (l >= Seg[R(k)].st){ Modify(l, r, val, R(k)); } else{ Modify(l, Seg[L(k)].ed, val, L(k)); Modify(Seg[R(k)].st, r, val, R(k)); } pushUp(k); } void Modify(int u, int v,int val){ int f1 = top[u], f2 = top[v]; while (f1 != f2){ if (deep[f1] < deep[f2]){ swap(f1, f2); swap(u, v); } Modify(id[f1], id[u], val,1); u = fa[f1]; f1 = top[u]; } if (deep[u] > deep[v]){ swap(u, v); } Modify(id[u], id[v],val, 1); } Node Query(int l, int r, int k){ //求区间有多少段颜色。并且保存这段区间左端点和右端点的颜色 if (Seg[k].st == l&&Seg[k].ed == r){ return Seg[k]; } Node ans; pushDown(k); if (r <= Seg[L(k)].ed){ ans = Query(l, r, L(k)); } else if (l >= Seg[R(k)].st){ ans = Query(l, r, R(k)); } else{ Node ansL= Query(l, Seg[L(k)].ed, L(k)),ansR=Query(Seg[R(k)].st,r,R(k)); ans.val = ansL.val + ansR.val; ans.lcolor = ansL.lcolor; ans.rcolor = ansR.rcolor; if (ansL.rcolor == ansR.lcolor){ ans.val--; } } pushUp(k); return ans; } int Query(int u, int v){ int ans = 0,Lrc=-1,Rlc=-1; //保存上次询问时两端区间查询时deep比较小的点的颜色,因为可能两端同时上升爬 int f1 = top[u], f2 = top[v]; while (f1 != f2){ if (deep[f1] < deep[f2]){ swap(f1, f2); swap(u, v); swap(Lrc, Rlc); } Node temp = Query(id[f1], id[u], 1); ans += temp.val - (Lrc == temp.rcolor); u = fa[f1]; f1 = top[u]; Lrc = temp.lcolor; } if (deep[u] > deep[v]){ swap(u, v); swap(Lrc, Rlc); } Node temp = Query(id[u], id[v], 1); ans += temp.val - (temp.rcolor == Rlc) - (temp.lcolor == Lrc); return ans; } int main(){ //#ifdef kirito // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); //#endif // int start = clock(); int n,m; while (~scanf("%d%d",&n,&m)){ Init(); for (int i = 1; i <= n; i++){ scanf("%d", &value[i]); } for (int i = 1; i < n; i++){ int u, v; scanf("%d%d", &u, &v); add(u, v); add(v, u); } DFS1(1, 1, 0); DFS2(1, 1); Build(1, n, 1); while (m--){ char ope[10]; int u, v, w; scanf("%s", ope); switch (ope[0]){ case 'C': scanf("%d%d%d", &u, &v, &w); Modify(u, v, w); break; default: scanf("%d%d", &u, &v); printf("%d\n", Query(u, v)); break; } } } //#ifdef LOCAL_TIME // cout << "[Finished in " << clock() - start << " ms]" << endl; //#endif return 0; }