nssl 1486.旅游

D e s c r i p t i o n Description Description

给定一张无向图 G { n , m } G\{n,m\} G{n,m},每条边有一个权值 d i d_i di
q q q次询问,每次给定一个 x x x,规定所有 d i ≤ x d_i\leq x dix的是可以走的,求有多少点对 ( i , j ) (i,j) (i,j)满足 i i i可以走到 j j j

数据范围: n ≤ 2 × 1 0 4 , m ≤ 1 0 5 , q ≤ 5 × 1 0 3 n\leq 2\times 10^4,m\leq 10^5,q\leq 5\times 10^3 n2×104,m105,q5×103


S o l u t i o n Solution Solution

首先对 x x x排序,再对 d i d_i di排序,问题就转化成为依次开放一些边,求连通块内路径数
用并查集维护即可

时间复杂度: O ( m l o g m + q l o q q α ( n ) + n α ( n ) ) O(mlogm+qloqq\alpha (n)+n\alpha(n)) O(mlogm+qloqqα(n)+nα(n))


C o d e Code Code

#include
#include
#include
#define LL long long
using namespace std;int n,m,q,T,f[20010],siz[20010],ans[5010],nowsum;
struct node{int from,to,w;}e[100010];
struct data{int id,x;}qs[5011];
inline bool cmp(node x,node y){return x.w<y.w;}
inline bool cop(data x,data y){return x.x<y.x;}
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
signed main()
{
	T=read();
	while(T--)
	{
		n=read();m=read();q=read();
		for(register int i=1;i<=n;i++) f[i]=i,siz[i]=1;
		for(register int i=1;i<=m;i++) e[i].from=read(),e[i].to=read(),e[i].w=read();
		for(register int i=1;i<=q;i++) qs[i].x=read(),qs[i].id=i;
		sort(e+1,e+1+m,cmp);
		sort(qs+1,qs+1+q,cop);
		nowsum=0;
		for(register int i=1,j=0;i<=q;i++)
		{
			while(e[j+1].w<=qs[i].x&&j<m)
			{
				j++;
				int fx=find(e[j].from),fy=find(e[j].to);
				if(fx==fy) continue;
				nowsum+=siz[fx]*siz[fy];
				if(fx<fy) f[fy]=fx,siz[fx]+=siz[fy];
				else f[fx]=fy,siz[fy]+=siz[fx];
			}
			ans[qs[i].id]=nowsum;
		}
		for(register int i=1;i<=q;i++) printf("%d\n",ans[i]*2);
	}
}

你可能感兴趣的:(并查集)