【Codeforces】Codeforces Beta Round #73(Div. 1) D. Theseus and labyrinth | DFS、并查集

74 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【Codeforces】Codeforces Beta Round #73(Div. 1) D. Theseus and labyrinth | DFS、并查集

题目链接

Problem - 87D - Codeforces

题目

A long time ago in some country in Asia were civil wars.

Each of nn cities wanted to seize power. That's why sometimes one city gathered an army and sent it to campaign against another city.

Road making was difficult, so the country had few roads, exactly n1n - 1. Also you could reach any city from any other city going on those roads.

Even during the war the Oriental people remain spiritually rich and appreciate the beauty of nature. And to keep the memory of this great crusade for the centuries to come, they planted one beautiful tree by the road on which the army spent most time. The Oriental people love nature, that's why if there were several such roads, then one tree was planted by each of them.

Recently, when the records of the war were found, it became clear that each city attacked each other one exactly once. There were exactly n×(n1)n\times(n - 1) attacks in total. Everyone has been wondering what road after those wars became the most beautiful, that is, by which road they planted the largest number of beautiful trees.

题目大意

有一颗带权无根树 nn 个点 n1n-1 条边,从点 xx 出发前往点 yy,将会在从 xxyy 的简单路径中长度最长的边上打一个标记,如果长度最长的边不止一条,在每一条最长边上都打标记。

xx 取遍 1 到 nn 的所有值,yy 取遍 1 到 nn 的所有值之后,标记最多的边上有多少个标记,有多少条标记最多的边,输出他们的编号。

思路

为每条边计算它作为最长边的路径条数。我们先解决这两种极端情况,然后结合这两种情况,我们将得到一个完整的解决方案。

第一种情况是所有边的权重相同。在这种情况下,我们可以通过 DFS 解决问题。对于每条边,我们只需要计算经过这条边的路径数。这个数是把这条边切掉之后,这条边两个端点分别属于的两个不同连通块中点数量的乘积。以随便一个点 gg 为根节点,把整棵树拎起来。记 valival_i 表示以 ii 为根节点的子树中节点的个数,我们就可以找到每条边两个端点中 valval 值更小的端点 xx,则它作为最长边的路径条数为 (valgvalx)×valx(val_g-val_x)\times val_x

第二种情况是当所有边的权重不同。在这种情况下,按照权重增加的顺序对边缘进行排序。最初,我们采用没有边的图。我们按照权重从小到大遍历每一条边,当前边作为最长边的路径条数为这条边两个端点所在的连通块的大小之积,计算后我们将两个连通块合并。合并连通块和统计大小可以用并查集来完成。

结合这两种情况,按权重升序添加边,每次我们以权值相等的边建图。将每个连通块分别以随便什么根 DFS 拎起来求 valval,计算答案。容易发现当我们计算完当前权重的边后,现在的连通块内部的形状已经变得无关紧要,我们只关心点的数量。此时我们把每个连通块合为一个点,再进行后续的建图。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=500001;
using LL=long long;
struct asdf{
	int x,y,val,id;
}rd[N];
int n,m,len;
int cmp(asdf a,asdf b){return a.val<b.val;}
int f[N];
int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}
LL cnt[N],siz[N],val[N],vis[N];
vector<int> e[N];
void clear(int i)
{
	for (int j=i;j<n&&rd[j].val==rd[i].val;++j)
	{
		e[find(rd[j].x)].clear();
		e[find(rd[j].y)].clear();
		val[f[rd[j].x]]=val[f[rd[j].y]]=0;
		vis[f[rd[j].x]]=vis[f[rd[j].y]]=0;
	}
}
LL dfs(int u)
{
	if (vis[u]) return val[u];
	vis[u]=1;
	for (auto v:e[u])
		if (!vis[v]) val[u]+=dfs(v);
	return val[u];
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
	{
		f[i]=i;
		siz[i]=1;
	}
	for (int i=1;i<n;++i) scanf("%d%d%d",&rd[i].x,&rd[i].y,&rd[i].val),rd[i].id=i;
	sort(rd+1,rd+n,cmp);
	LL fa,sn;
	for (int i=1,j;i<n;i=j)
	{
		clear(i);
		for (j=i;j<n&&rd[j].val==rd[i].val;++j)
		{
			rd[j].x=find(rd[j].x);
			rd[j].y=find(rd[j].y);
			e[rd[j].x].push_back(rd[j].y);
			e[rd[j].y].push_back(rd[j].x);
			val[rd[j].x]=siz[rd[j].x];
			val[rd[j].y]=siz[rd[j].y];
		}
		for (j=i;j<n&&rd[j].val==rd[i].val;++j)
		{
			siz[find(rd[j].x)]+=siz[find(rd[j].y)];
			f[f[rd[j].y]]=f[rd[j].x];
		}
		for (j=i;j<n&&rd[j].val==rd[i].val;++j)
		{
			fa=siz[find(rd[j].x)];
			dfs(rd[j].y);
			sn=min(val[rd[j].x],val[rd[j].y]);
			cnt[rd[j].id]=sn*(fa-sn);
		}
		
	}
	LL maxx=0;
	int num=0;
	for (int i=1;i<n;++i) 
		if (cnt[i]>maxx)
		{
			maxx=cnt[i];
			num=1;
		}
		else num+=(cnt[i]==maxx);
	cout<<maxx*2<<" "<<num<<endl;
	for (int i=1;i<n;++i)
		if (cnt[i]==maxx) cout<<i<<" ";
}