给出了一个有N个节点的树。 每个节点都有一个颜色配置项。 我们还被给予N-1个查询,并且在每个查询中,我们被告知要破坏一个以前未破坏的边。 每次我们破坏一条边时,我们必须报告断开连接的节点对的数量。 这里,两个节点i和j被称为断开的,如果在破坏之前,你可以使用尚未破坏的边从i到达j,并且如果ci=cj。
如何处理这个问题? 我的一些测试用例正在使用dfs运行,但有些显示超出了时间限制。 请帮帮忙
我的代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
int par[300002];
i nt sz[300002];
vector<pair<ll, ll>> arr;
ll n, i, a, b, x, y;
ll find(ll x)
{
if (x != par[x])
{
par[x] = find(par[x]);
}
return par[x];
}
bool same(ll x, ll y)
{
return find(x) == find(y);
}
void merge(ll x, ll y)
{
ll x1 = find(x);
ll y1 = find(y);
if (sz[x1] > sz[y1])
{
par[y] = x;
sz[x1] += sz[y1];
}
else
{
par[x] = y;
sz[y1] += sz[x1];
}
}
int main()
{
cin >> n;
vector<ll> ans(n + 1);
for (i = 1; i <= n; i++)
{
par[i] = i;
sz[i] = 1;
}
for (i = 1; i <= n - 1; i++)
{
cin >> a >> b;
arr.push_back(make_pair(a, b));
}
int c[n + 1];
for (i = 1; i <= n; i++)
{
cin >> c[i];
}
ans[n] = (n * (n - 1)) / 2;
for (i = n - 1; i >= 0; i--)
{
x = arr[i].first;
y = arr[i].second;
if (same(x, y))
{
ans[i] = ans[i + 1];
continue;
}
ll s1 = sz[find(x)];
ll s2 = sz[find(y)];
merge(x, y);
ans[i] = max(ans[i + 1] - (s1 * s2), 0LL);
}
int z;
for (i = 1; i <= n - 1; i++)
{
cin >> z;
cout << ans[z] << '\n';
}
}
我已经接受了一些测试案例,但是大部分案例都超过了时间限制。我想了很多关于如何优化的问题,但是无法做到。请帮助我
这是那些可以更容易地反过来解决的问题之一:
u
和v
,如果它们共享相同的颜色dsu
(又称union find
)数据结构来完成,对于每个节点u
我们保存节点的父节点和包含该节点的集合的大小(最初的父节点为-1或null,大小为1)u
和v
所属的树的大小来检查有多少路径会被破坏
总复杂度:O(n*logn)
*
(*假设DSU只使用1个优化(路径压缩/大小压缩),如果同时使用这两个优化,则速度会更快,如果不使用O(n^2)
)