题目描述 Description
毕业了,Xth很高兴,因为他要和他的 rabbit 去双人旅行了。他们来到了水城威尼
斯。众所周知(⊙﹏⊙b汗),这里的水路交通很发达,所以 xth 和 rabbit 只好坐
船穿梭于各个景点之间。但是要知道,rabbit 是会晕船的,看到她难受,xth 是会
心疼的。
已知城市中有n个景点,这些景点之间有m条双向水路,在每条水路上航行时
rabbit 都会有一个“晕船值”。旅行时,xth 会带着 rabbit 尽量选择晕船值小的路线
旅行。但是 rabbit 也是有一定忍耐限度度的,如果晕船值超过了她的忍耐度,xth
会果断决定放弃这条路线。
现在 xth 想进行若干次询问,给定 rabbit 的忍耐度,问还有多少对城市(x,y)间会存
在可行的旅行路线(如果(x,z)和(z, y)可行,则(x,y)可行,也就是说连通性是可传
递的)。
输入描述 Input Description
第 1 行三个正整数n、m、q,分别表示景点数量、水路数量和询问次数。
第 2 行到第m + 1行每行三个正整数x、y、w,表示x号景点和y号景点之间有一条
“晕船值”为w的双向水路。
第m + 2行至第m + q + 1行,每行一个正整数k,表示询问中给定的 rabbi忍耐度
为k。
输出描述 Output Description
共q行,对于每次询问做出回答。
样例输入 Sample Input
5 5 2
1 2 1
2 3 2
3 4 1
4 5 4
5 1 1
1
2
样例输出 Sample Output
4
10
数据范围及提示 Data Size & Hint
第一个询问:(1,2), (1,5), (2,5), (3,4)。其中(2,5)的具体走法为:2 − 1 − 5
第二个询问:(1,2), (1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (3,4), (3,5), (4,5)。其中(4,5)
的具体走法为:4 − 3 − 2 − 1 − 5
对于20%的数据满足n ≤ 20,m ≤ 40,q ≤ 40;
对于40%的数据满足n ≤ 1000,m ≤ 2000,q ≤ 1000;
对于60%的数据满足n ≤ 3000,m ≤ 6000,q ≤ 200000;
对于100%的数据满足n ≤ 100000,m ≤ 200000,q ≤ 200000。其他数不超过10
9。
询问有限制的题,先想离线
把所有询问从小到大排序,再跑最小生成树
保证当前生成树中的所有边小于目前询问
如果发现要添的边大了,更新目前询问的答案,处理下一组询问;
更新答案:对于新合并的两棵树A和B,对答案贡献为A的节点数*B的节点数
树的节点数在并查集合并时记录,因为每棵树标志元素是不变的,合并时标志元素的cnt加一下就好了
本来以为需要打高精……然而2000000^2好像longlong就能过
#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int MAXN = 200000 + 50;
LL n,m,q;
struct edge
{
LL f,t,v;
}l[MAXN << 1];
bool cmp(edge a,edge b)
{
return a.v < b.v;
}
struct zt
{
LL num,que,ans;
}Q[MAXN];
LL a,b,c;
LL ans = 0,cnt[MAXN],fa[MAXN],rank[MAXN];
void init(LL n)
{
for(LL i = 1;i <= n;i ++)
fa[i] = i,cnt[i] = 1;
}
LL find(LL x)
{
if(fa[x] == x)return x;
LL temp = find(fa[x]);
return fa[x] = temp;
}
void merge(LL a,LL b)
{
a = find(a);b = find(b);
LL temp = cnt[a] + cnt[b];
cnt[a] = cnt[b] = temp;
if(rank[a] < rank[b])swap(a,b);
fa[b] = a;
if(rank[a] == rank[b])rank[a] ++;
}
bool same(LL a,LL b)
{
return find(a) == find(b);
}
bool cmp1(zt a,zt b)
{
return a.que < b.que;
}
bool cmp2(zt a,zt b)
{
return a.num < b.num;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&q);
init(n);
for(LL i = 1;i <= m;i ++)
{
scanf("%lld%lld%lld",&a,&b,&c);
l[i] = (edge){a,b,c};
}
sort(l + 1,l + m + 1,cmp);
for(LL i = 1;i <= q;i ++)
{
scanf("%lld",&Q[i].que);
Q[i].num = i;
}
sort(Q + 1,Q + q + 1,cmp1);
LL head = 1;
for(LL i = 1;i <= m;i ++)
{
LL f = l[i].f,t = l[i].t;
while(l[i].v > Q[head].que)
{
Q[head].ans = ans;
head ++;
}
if(!same(f,t))
{
f = find(f);
t = find(t);
ans += cnt[f] * cnt[t];
merge(f,t);
}
}
while(head <= q)
{
Q[head ++].ans = ans;
}
sort(Q + 1,Q + q + 1,cmp2);
for(int i = 1;i <= q;i ++)
{
printf("%lld\n",Q[i].ans);
}
return 0;
}