2243: [SDOI2011]染色
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 8608 Solved: 3221
[ Submit][ Status][ Discuss]
Description
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
Input
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
Output
对于每个询问操作,输出一行答案。
Sample Input
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5
Sample Output
3
1
2
1
2
HINT
数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct tree{
int pre, suf, sm, lazy;
}c[maxn * 4];
int pre[maxn], sz[maxn], dep[maxn], son[maxn], top[maxn], id[maxn], val[maxn], a[maxn], tot;
vector<vector<int> >g(maxn);
void dfs1(int x, int fa, int d){
dep[x] = d;
sz[x] = 1;
pre[x] = fa;
son[x] = 0;
int cur;
for(int i = 0; i < g[x].size(); ++i){
cur = g[x][i];
if(cur == fa) continue;
dfs1(cur, x, d + 1);
sz[x] += sz[cur];
if(sz[son[x]] < sz[cur]){
son[x] = cur;
}
}
}
void dfs2(int x, int tp){
top[x] = tp;
id[x] = ++tot;
if(son[x]){
dfs2(son[x], tp);
}
int cur;
for(int i = 0; i < g[x].size(); ++i){
cur = g[x][i];
if(cur == pre[x] || cur == son[x]) continue;
dfs2(cur, cur);
}
}
void build(int o, int l, int r){
c[o].lazy = -1;
if(l == r){
c[o].pre = c[o].suf = val[l];
c[o].sm = 1;
return;
}
int mid = l + r >> 1;
build(o << 1, l, mid);
build(o << 1 | 1, mid + 1, r);
c[o].pre = c[o << 1].pre;
c[o].suf = c[o << 1 | 1].suf;
c[o].sm = c[o << 1].sm + c[o << 1 | 1].sm - (c[o << 1].suf == c[o << 1 | 1].pre);
}
void pushdown(int o, int l, int r){
if(c[o].lazy != -1){
c[o << 1].pre = c[o << 1].suf = c[o].lazy;
c[o << 1 | 1].pre = c[o << 1 | 1].suf = c[o].lazy;
c[o << 1].sm = c[o << 1 | 1].sm = 1;
c[o << 1].lazy = c[o << 1 | 1].lazy = c[o].lazy;
c[o].lazy = -1;
}
}
int query(int o, int l, int r, int L, int R, int op){
if(l >= L && r <= R){
if(op == 1){
return c[o].sm;
}
if(op == 2){
return c[o].pre;
}
if(op == 3){
return c[o].suf;
}
}
pushdown(o, l, r);
int mid = l + r >> 1;
if(mid < L) return query(o << 1 | 1, mid + 1, r, L, R, op);
else if(mid >= R) return query(o << 1, l, mid, L, R, op);
else{
if(op == 2) return query(o << 1, l, mid, L, R, op);
if(op == 3) return query(o << 1 | 1, mid + 1, r, L, R, op);
if(op == 1){
int ans = query(o << 1, l, mid, L, R, op) + query(o << 1 | 1, mid + 1, r, L, R, op);
ans -= (query(o << 1, l, mid, L, R, 3) == query(o << 1 | 1, mid + 1, r, L, R, 2));
return ans;
}
}
}
int getsm(int x, int y){
int tp1 = top[x], tp2 = top[y];
int ans = 0;
int cx = -1, cy = -1; //这两个值用来标记上一段末的颜色,这里值得仔细想想,推敲一下
while(tp1 != tp2){
if(dep[tp1] < dep[tp2]){
swap(tp1, tp2);
swap(x, y);
swap(cx, cy);
}
ans += query(1, 1, tot, id[tp1], id[x], 1);
if(query(1, 1, tot, id[tp1], id[x], 3) == cx){
ans--;
}
cx = query(1, 1, tot, id[tp1], id[x], 2);
x = pre[tp1];
tp1 = top[x];
}
if(dep[x] < dep[y]){
swap(x, y);
swap(cx, cy);
}
ans += query(1, 1, tot, id[y], id[x], 1);
ans -= (cx == query(1, 1, tot, id[y], id[x], 3)) + (cy == query(1, 1, tot, id[y], id[x], 2));
return ans;
}
void update(int o, int l, int r, int L, int R, int v){
if(l >= L && r <= R){
c[o].pre = c[o].suf = c[o].lazy = v;
c[o].sm = 1;
return;
}
int mid = l + r >> 1;
pushdown(o, l, r);
if(mid >= L) update(o << 1, l, mid, L, R, v);
if(mid < R) update(o << 1 | 1, mid + 1, r, L, R, v);
c[o].pre = c[o << 1].pre;
c[o].suf = c[o << 1 | 1].suf;
c[o].sm = c[o << 1].sm + c[o << 1 | 1].sm;
c[o].sm -= (c[o << 1].suf == c[o << 1 | 1].pre);
}
void change(int x, int y, int v){
int tp1 = top[x], tp2 = top[y];
while(tp1 != tp2){
if(dep[tp1] < dep[tp2]){
swap(tp1, tp2);
swap(x, y);
}
update(1, 1, tot, id[tp1], id[x], v);
x = pre[tp1];
tp1 = top[x];
}
if(dep[x] < dep[y]){
swap(x, y);
}
update(1, 1, tot, id[y], id[x], v);
}
int main(){
int n, m, x, y, v;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++i){
scanf("%d", &a[i]);
a[i]++;
}
for(int i = 1; i < n; ++i){
scanf("%d %d", &x, &y);
g[x].push_back(y);
g[y].push_back(x);
}
tot = 0;
dfs1(1, 0, 1);
dfs2(1, 1);
for(int i = 1; i <= n; ++i){
val[id[i]] = a[i];
}
build(1, 1, tot);
char s[2];
for(int i = 1; i <= m; ++i){
scanf("%s", s);
if(s[0] == 'Q'){
scanf("%d %d", &x, &y);
printf("%d\n", getsm(x, y));
}
else{
scanf("%d %d %d", &x, &y, &v);
v++;
change(x, y, v);
}
}
}
/*
题意:一棵树,1e5个节点,每个节点有初始颜色,用数字表示,1e5次操作,
每次操作要么将两点间路径上的点改成同一给定颜色,要么询问两点之间的
有多少个颜色段。
思路;这题比较明显了,树链剖分+线段树维护区间颜色段的数量,唯一需要主要的
地方是在线段树上分治的时候,注意两个线段前后如果颜色相同不能重复计算。
还有一个要注意的地方是,在树链的区间对应到线段树上的时候,上面的节点即高度小
的节点的标号更小,在线段树中是靠左的,这个在统计颜色的时候要特别注意。
*/