洛谷P2486 [SDOI2011]染色
标签
- 树剖
- 线段树
- 区间染色
简明题意
- 给一棵树,每个节点都拥有自己的颜色,需要你支持两种操作
- 修改:将u–v路径上的所有节点的颜色改为c
- 查询:查询u–v路径上有多少个颜色段
思路
- 树剖,剖完线段树维护颜色。修改change创造做很简单,改就完了,查询的操作稍微复杂点,就是比如树上的路径时这样的:1-2-3-4-10-11,查1–11的颜色段,显然1-4的颜色和10-11的颜色查完后,不能直接相加,因为4的颜色可能和10的颜色相同,这样的话,只需要多写一个单点查询颜色的函数,再加一句判断,如果颜色相同,答案-1就可以了。
注意事项
- 除了上面的情况,还有一种情况需要注意。就是线段树ask的时候,如果信息是从左右子树合并的话,是不能直接返回左右子树中颜色段的数量的和的。因为,可能左子树的右端点颜色和右子树的左端点颜色可能相同,这个时候又需要-1了。这里我判断左子树的右端点颜色和右子树的左端点颜色是否相同的方法是:线段树ask函数直接返回一个Node,得到了Node再去判断
总结
- 无
AC代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, a[maxn];
vector<int> g[maxn];
int dep[maxn], fa[maxn], siz[maxn], son[maxn];
void dfs1(int u, int f, int deep)
{
dep[u] = deep;
fa[u] = f;
siz[u] = 1;
int max_son = -1;
for (auto& v : g[u])
if (v != f)
{
dfs1(v, u, deep + 1);
siz[u] += siz[v];
if (siz[v] > max_son)
max_son = siz[v], son[u] = v;
}
}
int id[maxn], cnt, top[maxn], w[maxn];
void dfs2(int u, int topf)
{
id[u] = ++cnt;
top[u] = topf;
w[cnt] = a[u];
if (son[u])
{
dfs2(son[u], topf);
for (auto& v : g[u])
if (v != fa[u] && v != son[u])
dfs2(v, v);
}
}
struct Node
{
int l, r, sum, lc, rc;
int tag;
Node(int sum, int lc, int rc) : sum(sum), lc(lc), rc(rc) {}
Node(){}
};
Node tree[maxn * 4];
void spread(int o)
{
if (tree[o].tag)
{
tree[o].lc = tree[o].rc = tree[o].tag;
tree[o].sum = 1;
if (tree[o].l != tree[o].r)
tree[o * 2].tag = tree[o * 2 + 1].tag = tree[o].tag;
tree[o].tag = 0;
}
}
void update(int o)
{
if (tree[o].l != tree[o].r)
{
spread(o * 2), spread(o * 2 + 1);
tree[o].sum = tree[o * 2].sum + tree[o * 2 + 1].sum;
tree[o].lc = tree[o * 2].lc, tree[o].rc = tree[o * 2 + 1].rc;
if (tree[o * 2].rc == tree[o * 2 + 1].lc)
tree[o].sum--;
}
}
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
if (l == r)
{
tree[o].lc = tree[o].rc = w[l];
tree[o].sum = 1;
return;
}
int mid = (l + r) / 2;
build(o * 2, l, mid);
build(o * 2 + 1, mid + 1, r);
update(o);
}
void change(int o, int l, int r, int c)
{
spread(o);
if (tree[o].l == l && tree[o].r == r)
{
tree[o].tag = c;
spread(o);
return;
}
int mid = (tree[o].l + tree[o].r) / 2;
if (r <= mid)
change(o * 2, l, r, c);
else if (l > mid)
change(o * 2 + 1, l, r, c);
else
change(o * 2, l, mid, c), change(o * 2 + 1, mid + 1, r, c);
update(o);
}
Node ask(int o, int l, int r)
{
spread(o);
if (tree[o].l == l && tree[o].r == r)
return tree[o];
int mid = (tree[o].l + tree[o].r) / 2;
if (r <= mid)
return ask(o * 2, l, r);
else if (l > mid)
return ask(o * 2 + 1, l, r);
else
{
Node ll = ask(o * 2, l, mid);
Node rr = ask(o * 2 + 1, mid + 1, r);
if (ll.rc == rr.lc)
return Node(ll.sum + rr.sum - 1, ll.lc, rr.rc);
else
return Node(ll.sum + rr.sum, ll.lc, rr.rc);
}
}
int ask_co(int o, int x)
{
spread(o);
if (tree[o].l == tree[o].r)
return tree[o].lc;
int mid = (tree[o].l + tree[o].r) / 2;
if (x <= mid)
return ask_co(o * 2, x);
else
return ask_co(o * 2 + 1, x);
}
void solve()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v), g[v].push_back(u);
}
dfs1(1, 1, 1);
dfs2(1, 1);
build(1, 1, n);
while (m--)
{
char cmd[10];
scanf("%s", cmd);
if (cmd[0] == 'Q')
{
int u, v;
scanf("%d%d", &u, &v);
int cnt = 0;
while (top[u] != top[v])
{
if (dep[top[v]] < dep[top[u]]) swap(u, v);
cnt += ask(1, id[top[v]], id[v]).sum;
if (ask_co(1, id[top[v]]) == ask_co(1, id[fa[top[v]]])) cnt--;
v = fa[top[v]];
}
printf("%d\n", cnt + ask(1, min(id[u], id[v]), max(id[u], id[v])).sum);
}
else
{
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
while (top[u] != top[v])
{
if (dep[top[v]] < dep[top[u]]) swap(u, v);
change(1, id[top[v]], id[v], c);
v = fa[top[v]];
}
change(1, min(id[u], id[v]), max(id[u], id[v]), c);
}
}
}
int main()
{
freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
双倍经验
- 无