题目
样例
数据范围
解题
乍一看是图,但是并查集。其实画个图就知道了
用微软白板画的,鼠标作画。
可以看到3和5之间是5705的人气值,当询问6000时,就会有
(5,3)和(3,5)这两个组合满足题目条件,像这种求连通的题呢,就可以用并查集了,不难发现,3和5一开始属于两个并查集,但满足条件之后贡献出了两个答案,那么3和5这个并查集与2这个并查集合并之后的贡献是多少呢?
是2乘1再考虑反方向走再乘2对吧,所以现在就有了6个答案(2+4)。
但是这题如果每次询问都去重做一遍,可能会爆掉,所以考虑离线做法,具体看代码
代码
#include<bits/stdc++.h>
using namespace std;
int fa [400086];//fa[i]是i的大哥
int cnt[400086];//cnt[i]是i的帮派规模
int result=0;//单次结果
int ans[400086];
struct EDGE
{
int u;
int v;
int w;
}edge[100086];//从u走到v的人气值w
struct QUESTION
{
int w;
int num;
}que[100086];//问的人气是多少,第几次询问
int finding(int x)//找老大
{
if(fa[x]==x)return x;//如果这个人只服自己,说明他是老大
else return fa[x]=finding(fa[x]);//如果他的内心还有一个伟岸存在,那么去寻找他
}
void merging(int x,int y)//帮派合并
{
int i=finding(x);
int j=finding(y);//找老大
if(i==j)return ;//大水淹了龙王庙,不用合了
result+=cnt[i]*cnt[j]*2;//不难看出它们对于路线的贡献
fa[i]=j;//i认j为老大
cnt[j]+=cnt[i];//规模扩大
cnt[i]=0;//被合并了
}
bool cmp1(EDGE x,EDGE y)
{
return x.w<y.w;
}//排序一下更好找路
bool cmp2(QUESTION x,QUESTION y)
{
return x.w<y.w;
}//排序一下更好问路
int main()
{
int test;
cin>>test;
while(test--)
{
int n,m,q;
cin>>n>>m>>q;
result=0;
for (int i=1;i<=n;i++)
{
fa[i]=i;
cnt[i]=1;
}//初始化
for (int i=1;i<=m;i++)
{
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
for (int i=1;i<=q;i++)
{
cin>>que[i].w;
que[i].num=i;
}
sort(edge+1,edge+1+m,cmp1);
sort(que +1,que +1+q,cmp2);
int j=1;//到第几条路
for (int i=1;i<=q;i++)//询问q次
{
while((j<=m)&&(edge[j].w<=que[i].w))
{//如果路还没走完并且这条路人气值不超过询问的人气值
merging(edge[j].u,edge[j].v);
j++;
//合并并记录又走完了一条路
}
ans[que[i].num]=result;//当没有路符合条件或走完时,将答案存进去
}
for (int i=1;i<=q;i++)
{
cout<<ans[i]<<endl;
}
}
return 0;
}