NOIP2023模拟6联测27 B. 旅行
题目大意
有一棵 n n n 个点, n n n 条边的基环树,每条边有一种颜色。
现在要修改 n n n 次颜色,询问每次修改颜色后的连通块的数量。
思路
我们发现修改就是先删除,再添加边。
考虑用一个 m a p i , j map_{i , j} mapi,j 维护与点 i i i 相连的,颜色为 j j j 的边的数量。
对于添加一条边 x , y , w {x , y , w} x,y,w
- 如果 x , y x , y x,y 都有其他的边颜色为 w w w ,那么就要 a n s − − ans-- ans−−
- 如果只有一个点有,那么 a n s ans ans 不变
- 如果两个点都没有,那么 a n s + + ans ++ ans++
- 如果 x , y x , y x,y 是环上的点且环上的边都是同一种颜色,那么 a n s ans ans 要加一,因为上面减了一
对于删除一条边 x , y , w x , y , w x,y,w 和加边操作类似,但是注意如果 x , y x , y x,y 在环上且这个环的颜色是同一种,那么连通块数量不变。
code
#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
using namespace std;
const int N = 3e5 + 5;
int hd[N] , cnt , stk[N] , top , sum[N] , dep[N] , cir[N] , cir1 , ans , n , m , flg[N << 1];
struct E {
int to , nt , w;
} e[N << 1];
map<int , int> mp[N] , vis[N];
void add (int x , int y , int z) { e[++cnt].to = y , e[cnt].nt = hd[x] , e[cnt].w = z , mp[x][y] = cnt , hd[x] = cnt; }
void dfs (int x , int fa) {
dep[x] = dep[fa] + 1;
int y;
stk[++top] = x;
for (int i = hd[x] ; i ; i = e[i].nt) {
y = e[i].to;
if (y == fa) continue;
if (dep[y] && dep[y] < dep[x]) {
while (stk[top] != y) {
cir[++cir1] = stk[top];
top--;
}
cir[++cir1] = y;
}
if (!dep[y]) {
dfs (y , x);
}
}
if (stk[top] == x)
top --;
}
void Insert (int x , int y , int w , int z) {
int tmp = (vis[x][w] != 0) + (vis[y][w] != 0);
vis[x][w] ++;
vis[y][w] ++;
if (z)
++ sum[w];
if (z && sum[w] == cir1)
tmp --;
ans += 1 - tmp;
}
void del (int x , int y , int w , int z) {
vis[x][w] -- , vis[y][w] --;
int tmp = (vis[x][w] != 0) + (vis[y][w] != 0);
if (z && sum[w] == cir1)
tmp--;
if (z)
sum[w] --;
ans -= 1 - tmp;
}
int main () {
freopen ("tour.in" , "r" , stdin);
freopen ("tour.out" , "w" , stdout);
int T , u , v , w , x;
scanf ("%d" , &T);
while (T --) {
scanf ("%d%d" , &n , &m);
cnt = 1;
cir1 = top = 0;
ans = 0;
// dep[1] = 1;
fu (i , 1 , n) hd[i] = dep[i] = sum[i] = 0 , mp[i].clear() , vis[i].clear();
fu (i , 1 , n) {
scanf ("%d%d%d" , &u , &v , &w);
add (u , v , w) , add (v , u , w);
}
fu (i , 2 , cnt) flg[i] = 0;
dfs (1 , 0);
// cout << cir1 << "\n";
// continue;
fu (i , 1 , cir1)
flg[mp[cir[i]][cir[i % cir1 + 1]]] = flg[mp[cir[i]][cir[i % cir1 + 1]] ^ 1] = 1;
for (int i = 2 ; i <= cnt ; i += 2)
Insert (e[i].to , e[i ^ 1].to , e[i].w , flg[mp[e[i].to][e[i ^ 1].to]]);
while (m --) {
scanf ("%d%d%d" , &u , &v , &w);
x = mp[u][v];
del (u , v , e[x].w , flg[x]);
Insert (u , v , w , flg[x]);
e[x].w = e[x ^ 1].w = w;
printf ("%d\n" , ans);
}
}
return 0;
}