题目链接:
题目大意:
先给一棵n
个点的数,
“u,v”
表示
v
是
m
个操作:
“Txy”
表示把 以
x
为根的子树全部染成
“Cx”
表示询问
x
号节点当前是哪种颜色?
数据范围:
n,m≤500001≤x≤n,1≤y≤109
解题思路:
套路题,搞出DFS序,然后用线段树成段更新,以及单点查询就over!
外话:
学到了怎么搞DFS序,目前我知道两种方法:
一种是直接记每个节点的开始和结束的时间戳,其实就是把节点按照DFS的先后顺序(这个好像叫先序遍历)进行标号,像这样(样例):
操作节点3就相当于修改区间[3, 5]。
另一种就是记每个节点开始的时间戳以及 以这个节点为根的子树的size。
两种方法大同小异,最终目的是要将树对应为区间,将树上的操作转化为区间上的修改,好用线段树来解决问题。
详见代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 50005;
int T;
int n, m;
int all, time, cas;
int pre[2 * MaxN + 5], last[MaxN + 5], other[2 * MaxN + 5];
int fa[MaxN + 5];
int left[MaxN + 5], right[MaxN + 5];
struct segtree
{
int l, r;
int task; //当前区间的颜色
}tree[4 * MaxN + 5];
int lazy[4 * MaxN + 5];//lazy[rt]表示rt节点所管辖的区间 的颜色
//感觉有点多余,好像用那个task也可以实现,不过没写对
void build(int x, int y) { //建树
pre[++all] = last[x];
last[x] = all;
other[all] = y;
}
int find(int x) { //找到给的这棵树的根节点
int father = fa[x];
while(x != father) {
x = father;
father = fa[father];
}
return x;
}
void Dfs(int x, int fa) {
int ed = last[x], dr;
++time;
left[x] = time; //开始时间,对应区间左端点
while(ed != -1) {
dr = other[ed];
if(dr != fa)
Dfs(dr, x);
ed = pre[ed];
}
right[x] = time; //结束时间,对应区间右端点
}
void Build(int rt, int l, int r) //建线段树
{
tree[rt].l = l, tree[rt].r = r;
tree[rt].task = -1; //初始化为-1
lazy[rt] = -1;
if(l == r) return ;
int mid = (l + r) >> 1;
Build(rt << 1, l, mid);
Build(rt << 1 | 1, mid + 1, r);
}
void push_down(int rt) { //下放标记
if(lazy[rt] != -1) {
tree[rt << 1].task = lazy[rt];
tree[rt << 1 | 1].task = lazy[rt];
lazy[rt << 1] = lazy[rt];
lazy[rt << 1 | 1] = lazy[rt];
lazy[rt] = -1;
}
}
void update(int rt, int L, int R, int t)
{
if(L <= tree[rt].l && tree[rt].r <= R) {
lazy[rt] = t; //区间修改
tree[rt].task = t;
return;
}
push_down(rt);
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(L <= mid) update(rt << 1, L, R, t);
if(R > mid) update(rt << 1 | 1, L, R, t);
}
int query(int rt, int pos) {
if(tree[rt].l == pos && pos == tree[rt].r) {
return tree[rt].task;
}
push_down(rt);
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(pos <= mid) return query(rt << 1, pos);
else return query(rt << 1 | 1, pos);
}
int main()
{
cas = 0;
scanf("%d", &T);
while(T--) {
time = 0;
all = -1; memset(last, -1, sizeof(last));
scanf("%d", &n);
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= n - 1; i++) {
int u, v;
scanf("%d %d", &u, &v);
fa[u] = v;
build(v, u);
}
int root = find(1);//可在1~n中随便找一点
//printf("root = %d\n", root);
Dfs(root, 0);
Build(1, 1, time);
printf("Case #%d:\n", ++cas);
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
char op;
int x, y;
scanf(" %c", &op);
if(op == 'C') {
scanf("%d", &x);
printf("%d\n", query(1, left[x]));//单点查询
}
else if(op == 'T') {
scanf("%d %d", &x, &y);
update(1, left[x], right[x], y);//更新区间
}
}
memset(tree, 0, sizeof(tree));
memset(left, 0, sizeof(left));
memset(right, 0, sizeof(right));
memset(pre, 0, sizeof(pre));
memset(other, 0, sizeof(other));
}
return 0;
}
求开始时间戳和子树size的也大同小异,一并贴上:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 50005;
int T;
int n, m;
int all, time, cas;
int pre[2 * MaxN + 5], last[MaxN + 5], other[2 * MaxN + 5];
int fa[MaxN + 5];
int start[MaxN + 5], size[MaxN + 5];
struct segtree
{
int l, r;
int task;
}tree[4 * MaxN + 5];
int lazy[4 * MaxN + 5];
void build(int x, int y) {
pre[++all] = last[x];
last[x] = all;
other[all] = y;
}
int find(int x) {
int father = fa[x];
while(x != father) {
x = father;
father = fa[father];
}
return x;
}
void Dfs(int x, int fa) {
int ed = last[x], dr;
++time;
start[x] = time;
while(ed != -1) {
dr = other[ed];
if(dr != fa) {
Dfs(dr, x);
size[x] += size[dr];
}
ed = pre[ed];
}
}
void Build(int rt, int l, int r)
{
tree[rt].l = l, tree[rt].r = r;
tree[rt].task = -1;
lazy[rt] = -1;
if(l == r) return ;
int mid = (l + r) >> 1;
Build(rt << 1, l, mid);
Build(rt << 1 | 1, mid + 1, r);
}
void push_down(int rt) {
if(lazy[rt] != -1) {
tree[rt << 1].task = lazy[rt];
tree[rt << 1 | 1].task = lazy[rt];
lazy[rt << 1] = lazy[rt];
lazy[rt << 1 | 1] = lazy[rt];
lazy[rt] = -1;
}
}
void update(int rt, int L, int R, int t)
{
if(L <= tree[rt].l && tree[rt].r <= R) {
lazy[rt] = t;
tree[rt].task = t;
return;
}
push_down(rt);
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(L <= mid) update(rt << 1, L, R, t);
if(R > mid) update(rt << 1 | 1, L, R, t);
}
int query(int rt, int pos) {
if(tree[rt].l == pos && pos == tree[rt].r) {
return tree[rt].task;
}
push_down(rt);
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(pos <= mid) return query(rt << 1, pos);
else return query(rt << 1 | 1, pos);
}
int main()
{
cas = 0;
scanf("%d", &T);
while(T--) {
time = 0;
all = -1; memset(last, -1, sizeof(last));
scanf("%d", &n);
for(int i = 1; i <= n; i++) fa[i] = i, size[i] = 1;
for(int i = 1; i <= n - 1; i++) {
int u, v;
scanf("%d %d", &u, &v);
fa[u] = v;
build(v, u);
}
int root = find(1);
//printf("root = %d\n", root);
Dfs(root, 0);
Build(1, 1, time);
printf("Case #%d:\n", ++cas);
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
char op;
int x, y;
scanf(" %c", &op);
if(op == 'C') {
scanf("%d", &x);
printf("%d\n", query(1, start[x]));
}
else if(op == 'T') {
scanf("%d %d", &x, &y);
update(1, start[x], start[x] + size[x] - 1, y);
}
}
memset(tree, 0, sizeof(tree));
memset(start, 0, sizeof(start));
memset(size, 0, sizeof(size));
memset(pre, 0, sizeof(pre));
memset(other, 0, sizeof(other));
}
return 0;
}