City(gym103145)

City

https://codeforces.com/gym/103145/problem/K

题意:

由m条无向道路连接的n个城市,每条道路都有一个强度ki。敌人将进攻摧毁这些道路。当敌人发动伤害为x的攻击时,所有力量小于x的道路都将被摧毁。现在有Q个问题要问你,如果敌人发动伤害为pi的攻击,有多少对城市可以到达。城市x和城市y是可到达的,这意味着有一条从x到y的路径,并且该路径中每条道路的强度大于或等于pi。

题解:贪心+并查集

对于每个询问,我们可以看做每次把大于等于qi的路添加进去;

我们可以按照每条路的强度从大到小排序,q个询问也按照pi从大到小排序,这样既可以去掉重复的pi多次查询,也贪心去添加大于pi的路;然后每次添加路的时候,再使用并查集,就知道u,v是否已经在一个集合中,

如果在一个集合中,就不需要操作了;反之则需要把u,v添加到相同集合中;

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N],mp[N],n,m,q,ans[N],cnt,sum[N];//ans存储每次查询的对数,sum存储当前点为父节点的点数; 
struct node{
	ll u,v;
	ll w;
	bool operator <(const node &t){//每条路的强度从大到小排序
		return w>t.w;
	}
}edge[4*N];
struct node1{
	ll id;
	ll p;
	bool operator <(const node1 &t){//q个询问也按照pi从大到小排序
		return p>t.p;
	}
}nd[2*N];
void creat(){//初始并查集 
	for(ll i=1;i<=n;i++){
		a[i]=i; 
		sum[i]=1;
		ans[i]=0;
	}
}
int find(int x) {
    if(x!=a[x]) a[x]=find(a[x]);
	return a[x]; 
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		cin>>n>>m>>q;
		creat();
		cnt=0;
		nd[0].p=0,nd[0].id=0;
		for(int i=1;i<=m;i++){
			ll u,v,w;
			cin>>u>>v>>w;
			edge[++cnt]={u,v,w};
		}
		sort(edge+1,edge+1+cnt);
		for(int i=1;i<=q;i++){
			cin>>nd[i].p;
			nd[i].id=i;
		}
		sort(nd+1,nd+1+q);
		int j=1;
		for(int i=1;i<=q;i++){
			ll ans1=0;
			ans1=ans[nd[i-1].id];
			for(;j<=cnt;j++){
				if(edge[j].w<nd[i].p) break;//如果比当前查询的的要小就退出 
				ll u,v,w;
				u=edge[j].u;
				v=edge[j].v;
				w=edge[j].w;
				ll k1,k2;
				k1=find(u);
				k2=find(v);
				if(a[k1]!=a[k2]){//需要合并 
					ans1=ans1-(sum[a[k1]]*(sum[a[k1]]-1)/2+sum[a[k2]]*(sum[a[k2]]-1)/2);//减去原来的a[k1]的对数和原来的a[k2]的对数
					sum[a[k1]]+=sum[a[k2]];//把集合a[k2]的点数加入到a[k1]; 
					ans1+=sum[a[k1]]*(sum[a[k1]]-1)/2;//把新的a[k1]的对数重新计算加入 
					sum[a[k2]]=0;//把集合a[k2]变为0; 
					a[k2]=a[k1];//合并 
				}
			}
			ans[nd[i].id]=ans1;
		}
		for(int i=1;i<=q;i++){
			cout<<ans[i]<<endl;//输出每次查询 
		}
	}
	return 0;
}

上一篇:利用System.Net.Mail 的SmtpClient发送邮件


下一篇:golang文件存储纠删码实现