E 旅游方案(travel)
小 C 是一位著名的旅游家。他准备规划他在 K 国的旅游路线。
K 国有若干座城市,每个城市有一个唯一的编号。城市和城市之间有一些道路相连,每条 道路只在某一天开放。小 C 的旅游路线有包括三个参数:u, v, t,表示小 C 准备在第 t 天旅 游,路线的起点为城市 u,终点为城市 v。此外,旅游路线还必须满足以下要求:
• u < v,即出发城市的编号必须比终点城市的编号小;
• 在第 t 天中,小 C 能够沿着开放的道路从 u 走到 v。
请帮小 C 计算出不同的旅游方案数,即满足上述条件的三元组 (u, v, t) 的个数。小 C 只关 注路线的起点和终点,也就是说,即使在某一天小 C 能够以多种方式从起点城市 u 到终点城 市 v,在计算方案数时只计算一次。但是,如果能够在不同的几天从起点城市 u 到终点城市 v,在计算方案数时需要计算多次。
例如,假设 K 国有 3 座城市和 3 条道路,编号分别为 1,2,3,第一条道路连接城市 1 和 城市 2,第二条道路连接城市 2 和城市 3,第三条道路连接城市 1 和城市 3。其中,第 1 和第 2 条道路只在第 1 天开放,第 3 条道路只在第 2 天开放,如下图所示。
合法的旅游方案共有 4 种:
-
在第 1 天,以城市 1 为起点,城市 2 为终点;
-
在第 1 天,以城市 1 为起点,城市 3 为终点;
-
在第 1 天,以城市 2 为起点,城市 3 为终点;
-
在第 2 天,以城市 1 为起点,城市 3 为终点。
输入描述:
输入一个整数 n, m (2 ≤ n ≤ 100000, 1 ≤ m ≤ 100000),表示城市的数量和道路的数量。城市的编号从 1 到 n。
接下来的 m 行,每行包括三个整数 u, v, t (1 ≤ u < v ≤ n, 1 ≤ t ≤ 100000),表示在城市 u和城市 v 之间有一条双向道路,这条道路只在第 t 天开放。在同两座城市之间可能存在多条道路。
输出描述:
在一行内输出一个整数,表示不同的旅游方案数。
样例输入:
3 3
1 2 1
2 3 1
1 3 2
样例输出:
4
提示:
分析:
对于每一天可以通行的边,建立一个图,求出该图每一个联通块的顶点个数。对于每个节点的联通块, 从到 的路径有种 ,用ans将每天的每个联通块的可行路径数加起来即可。具体实现可分下列步骤
- 对所有的边,根据天数从小到大排序 (方便找到某一天的所有边的区间)
- 对于每一天的的所有边,用并查集的方法在祖先节点记录该集合的顶点数
- 在第二部的基础上,遍历所有这一天所有出现边的顶点,如果该顶点是祖先就统计该集合的顶点数,从而把 加到ans上。
##代码:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1e5+20;
struct Node{
int u,v,t;
}edge[maxn];
bool cmp(Node a,Node b)
{
return a.t<b.t;
}
int book[maxn];
int father[maxn],tot[maxn];//并查集 tot代表个数
int Find(int x)
{
if(x==father[x])
return x;
return father[x]=Find(father[x]);
}
ll calc(ll a)
{
return a*(a-1)/2;
}
ll solve(int l,int r)//对该区间内进行求解
{
for(int i=l;i<=r;++i)//初始化
{
father[edge[i].u]=edge[i].u;
father[edge[i].v]=edge[i].v;
tot[edge[i].u]=1;
tot[edge[i].v]=1;
}
/*并查集*/
for(int i=l;i<=r;++i)
{
int root1,root2;
root1=Find(edge[i].u);
root2=Find(edge[i].v);
if(root1!=root2)
{
tot[root1]+=tot[root2];
father[root2]=root1;
}
}
ll ans=0;
for(int i=l;i<=r;++i)
{
int u=edge[i].u;
if(father[u]==u&&tot[u]!=0)
{
ans+=calc(tot[u]);
tot[u]=0;
}
u=edge[i].v;
if(father[u]==u&&tot[u]!=0)
{
ans+=calc(tot[u]);
tot[u]=0;
}
}
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i)
{
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].t);
}
sort(edge,edge+m,cmp);
int l=0;
ll ans=0;
for(int i=0;i<m;++i)
{
if(i==m-1||edge[i].t!=edge[i+1].t)
{
ans+=solve(l,i);
l=i+1;
}
}
cout<<ans<<endl;
return 0;
}