Codeforces 2126F 1-1-1, Free Tree! 题解 [ 绿 ] [ 边转点 ] [ 根号分治 ] [ map ]
1-1-1, Free Tree!:简单题,这个 trick 好像和一道圆方树大 DS 差不多。
对于此类对树的边动态维护的题,有一个常见套路:确定一个根,然后把每一条边放在两个端点中深度更大的端点处统计,即放在子节点处统计。
这样做的优势是在修改某个点的时候,连向子节点的边可以统一处理掉,只需要单独修改它连向父亲的边即可。因为每个节点的父节点是唯一的,而子节点可能有很多个。
对于这道题也是一样,对于每个树上的节点开一个 map 快速维护子节点的颜色(即连向子节点的边的贡献),然后修改的时候单独修改连向父节点的边的贡献即可。注意还要修改父节点 map 中该子节点的贡献。
时间复杂度 \(O(n\log n)\),如果用 unordered_map 可以做到线性。
还有种做法是直接根号分治,对度数小于 \(\sqrt n\) 和大于 \(\sqrt n\) 的点分别处理修改即可,由于过于丑陋这里不再赘述。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<ll,ll>;
const int N = 200005;
ll n, q, a[N], f[N], c[N];
map<ll,ll> tot[N];
ll ans, sm;
vector<pi> g[N];
void dfs(int u, int fa)
{
f[u] = fa;
for(auto eg : g[u])
{
ll v = eg.fi, w = eg.se;
if(v == fa)continue;
dfs(v, u);
if(a[u] == a[v])ans += w;
tot[u][ a[v] ] += w;
c[v] = w;
}
}
void solve()
{
cin >> n >> q;
ans = sm = 0;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
g[i].clear();
tot[i].clear();
}
for(int i = 1; i < n; i++)
{
ll u, v, w;
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
sm += w;
}
dfs(1, 0);
while(q--)
{
ll u, x;
cin >> u >> x;
ans -= (a[u] == a[ f[u] ]) * c[u];
ans += (x == a[ f[u] ]) * c[u];
tot[ f[u] ][ a[u] ] -= c[u];
tot[ f[u] ][x] += c[u];
ans -= tot[u][ a[u] ];
ans += tot[u][x];
a[u] = x;
cout << sm - ans << '\n';
}
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)solve();
return 0;
}

浙公网安备 33010602011771号