题目大意:
就是现在给出一棵树, 以1为根, 树上的点都有自己的权值, 初始都没有被占领, 接下来3种操作
1. 从u到v的路径上的所有点被M占领
2. 某个点u被C占领
3. 以u为根的子树被M占领
每次操作后输出当前被M占领的那些点的点权和
大致思路:
很明显对于操作1和2用树链剖分很容易处理
对于第3种操作, 需要利用树的时间戳...于是这里写了一次dfs版的树链剖分
也没有什么特别了的吧...我线段树维护的时候记录当前区间总和, 和被占领的部分的总和, lazy标记时候被还原亦或被占领
对于第1种操作单次时间复杂度O((logn)^2), 另外两种单次时间复杂度都是O(logn)
代码如下:
Result : Accepted Memory : 13028 KB Time : 1528 ms
/* * Author: Gatevin * Created Time: 2015/9/10 23:13:50 * File Name: HLD_dfs.cpp */ #pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 100010 int n; int w[maxn]; struct Edge { int u, v, nex; Edge(int _u, int _v, int _nex) { u = _u, v = _v, nex = _nex; } Edge(){} }; Edge edge[maxn << 1]; int tot; int head[maxn]; void add_Edge(int u, int v) { edge[++tot] = Edge(u, v, head[u]); head[u] = tot; } int father[maxn]; int dep[maxn]; int siz[maxn]; int hson[maxn];//u的重儿子为hson[u], 为-1表示没有 int top[maxn];//top[u]表示结点u所在链的顶端结点 int L[maxn], R[maxn];//[L[u], R[u]]代表结点u的dfs序时间戳 int antiID[maxn];//antiID[x]表示树上那个结点在区间上位置是x //在dfs的方式下, 没有用id[x]表示结点x所在的区间位置, 因为L[]数组代替了这个作用 int tim;//时间戳时间 void dfs1(int now) { siz[now] = 1; hson[now] = -1; int nex; for(int i = head[now]; i + 1; i = edge[i].nex) if((nex = edge[i].v) != father[now]) { father[nex] = now; dep[nex] = dep[now] + 1; dfs1(nex); siz[now] += siz[nex]; if(hson[now] == -1 || siz[nex] > siz[hson[now]]) hson[now] = nex; } } void dfs2(int now, int tp) { L[now] = ++tim; top[now] = tp; antiID[tim] = now; if(hson[now] != -1) dfs2(hson[now], tp); int nex; for(int i = head[now]; i + 1; i = edge[i].nex) if((nex = edge[i].v) != father[now] && nex != hson[now]) dfs2(nex, nex); R[now] = tim; } void split() { tim = 0; father[1] = -1; dep[1] = 1; dfs1(1); dfs2(1, 1); } int ans; //Segment_Tree #define lson l, mid, rt << 1 #define rson mid + 1, r, rt << 1 | 1 int sum[maxn << 2];//当前区间总和 int occupy[maxn << 2];//当前结点代表的区间已经占领的和 int lazy[maxn << 2];// void pushUp(int rt) { sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; occupy[rt] = occupy[rt << 1] + occupy[rt << 1 | 1]; } void pushDown(int rt) { if(lazy[rt] == 1) { lazy[rt << 1] = lazy[rt << 1 | 1] = 1; occupy[rt << 1] = sum[rt << 1]; occupy[rt << 1 | 1] = sum[rt << 1 | 1]; lazy[rt] = 0; } if(lazy[rt] == 2) { lazy[rt << 1] = lazy[rt << 1 | 1] = 2; lazy[rt] = 0; occupy[rt << 1] = occupy[rt << 1 | 1] = 0; } } void build(int l, int r, int rt) { sum[rt] = 0, occupy[rt] = 0, lazy[rt] = 0; if(l == r) { sum[rt] = w[antiID[l]]; return; } int mid = (l + r) >> 1; build(lson); build(rson); pushUp(rt); } void update(int l, int r, int rt, int L, int R, int state)//state == 1更新为占用, 否则不占用 { if(l >= L && r <= R) { if(state == 1) { ans += sum[rt] - occupy[rt]; occupy[rt] = sum[rt]; lazy[rt] = 1; } else { ans -= occupy[rt]; occupy[rt] = 0; lazy[rt] = 2; } return; } int mid = (l + r) >> 1; pushDown(rt); if(mid >= L) update(lson, L, R, state); if(mid + 1 <= R) update(rson, L, R, state); pushUp(rt); } void modify(int x, int y)//更新x到y的路径为M占用 { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); update(1, n, 1, L[top[x]], L[x], 1); x = father[top[x]]; } if(dep[x] > dep[y]) swap(x, y); update(1, n, 1, L[x], L[y], 1); } int main() { int T; scanf("%d", &T); while(T--) { tot = 0; memset(head, -1, sizeof(head)); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", w + i); int u, v; for(int i = 1; i < n; i++) { scanf("%d %d", &u, &v); add_Edge(u, v); add_Edge(v, u); } split(); build(1, n, 1); ans = 0; int Q; scanf("%d", &Q); while(Q--) { int op; scanf("%d", &op); switch(op) { case 1: scanf("%d %d", &u, &v); modify(u, v);//u和v之间的路径被M占领 break; case 2: scanf("%d", &u); update(1, n, 1, L[u], L[u], 0);//u被C占领 break; case 3: scanf("%d", &u); update(1, n, 1, L[u], R[u], 1);//M占领u为根的子树 break; } printf("%d\n", ans); } } return 0; }