P8047 [COCI2015-2016#4] GALAKSIJA 题解

考虑倒着做,将删边变成加边,用并查集启发式合并维护并且在合并时更新贡献。

考虑现在有两个集合 SSTT,设两点树上距离为 dis(u,v)dis(u, v),则添加一条边时添加的贡献为 (u,v)(uS,vT)[dis(u,v)=0]\sum \limits_{(u,v)(u\in S,v\in T)}[dis(u,v)=0]

disdis 变成树上前缀和异或的形式,设 sus_u 表示 uu 到根的路径异或和。则原式变成 (u,v)(uS,vT)[su=sv]\sum \limits_{(u,v)(u\in S,v\in T)}[s_u=s_v]。并查集维护 gu,xg_{u,x} 表示以 uu 为根的并查集中 su=xs_u=xuu 的数量。启发式合并时枚举较小的那一个集合并累加贡献即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

struct Node
{
	int u, v, w;
	Node(int u, int v, int w): u(u), v(v), w(w){}
	Node() = default;
};

vector<Node> v;
int p[N];
int n;
long long ans = 0LL;
long long res[N];

int dis[N];
vector<pair<int, int>> G[N];

void dfs(int u, int f, int w)
{
	dis[u] = dis[f] ^ w;
	for (auto &[j, v] : G[u])
	{
		if (j ^ f) dfs(j, u, v);
	}
}

class Union_Find
{
public:
	int fa[N], sz[N];
	map<pair<int, int>, int> mp;
	vector<int> total[N];
	void Init()
	{
		for (int i = 0; i < N; i++) 
		{
			fa[i] = i, sz[i] = 1;
			if (i >= 1 && i <= n) mp[make_pair(i, dis[i])]++, total[i].emplace_back(i);
		} 
	}
	int find(int u)
	{
		return (fa[u] == u ? u : fa[u] = find(fa[u]));
	}
	void merge(int u, int v)
	{
		if ((u = find(u)) == (v = find(v))) return;
		if (sz[u] < sz[v]) swap(u, v);
		fa[v] = u;
		sz[u] += sz[v];
		for (auto &j : total[v])
		{
			total[u].emplace_back(j);
			ans += (mp.count(make_pair(u, dis[j])) ? mp[make_pair(u, dis[j])] : 0);
		}
		for (auto &j : total[v])
		{
			mp[make_pair(u, dis[j])]++;
			mp.erase(make_pair(j, dis[j]));
		}
		total[v].clear();
		total[v].shrink_to_fit(); 
	}
}uf;

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		Node g;
		cin >> g.u >> g.v >> g.w;
		v.emplace_back(g);
		G[g.u].emplace_back(make_pair(g.v, g.w));
		G[g.v].emplace_back(make_pair(g.u, g.w));
	}
	for (int i = 1; i < n; i++)
	{
		cin >> p[i];
	}
	dfs(1, 1, 0);
	uf.Init();
	for (int i = n - 1; i >= 1; i--)
	{
		uf.merge(v[p[i] - 1].u, v[p[i] - 1].v);
		res[i] = ans;
	}
	for (int i = 1; i < n; i++)
	{
		cout << res[i] << "\n";
	}
	cout << "0\n";
	return 0;	
}
posted @   HappyBobb  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示