zoukankan      html  css  js  c++  java
  • 5 7 级 错 位 打 ♂ 击 赛

    5 7 级 错 位 打 击 赛

    本篇博客并不会讲任何算法,所以极水

    博主写这篇博客完全是出于在机房颓废没有事情做,顺便整理一下模拟考试的题目,所以不会像之前那样完善

    T1

    传送门:https://www.luogu.com.cn/problem/U121235?contestId=31441

    显然,这是一个数据结构题(线段树),但是由于考试的时候没有推出标记下传的方法来,所以写了一个暴力,拿了40分的部分分

    因为本人写的代码实在太糟糕了(其实是懒得写,反正有学长的详细思路解释,我就直接(copy)啦),所以以下是学长的T1题解

    线段树板子题没人 (AC) 嘛?

    前言

    出题人本来打算给你们 (40pts) 的暴力分,结果没想到数据出水了,你们暴力竟然能拿 (70pts),给了你们一个 (T1) 很好做的样子。

    本着信心赛的原则,我还是很希望你们能多拿一点分数,给教练一个肯定的回答,让大家的分数好看点。

    还是很希望你们能认真地听完 (30pts) 的正解分。

    当然现在我加强了数据,暴力只能拿 (40pts) 了吧。

    (10pts)

    对于 (10)% 的数据,(x=0)

    操作 (1) 再怎么花里胡哨都是没用的,我们只需要考虑操作 (2) 即可。

    区间求和问题,可以直接用前缀和来做,时间复杂度 (Θ(n))

    期望得分 (:10pts)

    (20pts)

    对于另外 (10)% 的数据,(1<=l=r<=n)

    只是多了一个单点修改的操作,只需注意我们在修改的时候是 (-x),可以用线段树或树状数组来做,时间复杂度 (Θ(nlog_n))

    期望得分 (:20pts)

    (40pts)

    对于另外 (20)% 的数据,(1<=n,m<=5000)(-100<=a_i,x<=100)

    考虑到 (n)(m) 的范围很小,我们直接用最朴素的算法 "暴力" 直接做即可,时间复杂度 (Θ(nm))

    (100pts)

    对于 (100)%的数据,保证 (1<=n,m<=3*10^5)(-1000<=a_i,x<=1000)(1<=l,r<=n)

    看到这个数据范围,正解肯定是 (Θ(nlog_n)) 的数据结构无疑了 。

    由于出题人没有什么好的做法,那么这道题我们还是用线段树做吧。

    思考一下这道题和你们做的线段树板子有什么不同,很显然它的操作 (1) 变得更加毒瘤且恶心。

    但是换汤不换药,线段树板子的套路在这道题上还是可以应用的,那就是:懒标记

    回顾一下区间加、区间求和的懒标记:

    if(x<=l&&r<=y)
    {
    	lazy[node]+=v;
    	sum[node]+=(r-l+1)*v;
    	return ;
    }
    

    比着葫芦画瓢,我们做这道题的时候也设置一个懒标记:

    (lazy1[node]=k) 表示线段树中 (node) 节点所代表的区间进行了一次操作 (1),要时刻注意操作 (1) 是以 (-) 开始的。

    然后我们思考一下怎么 (Θ(1)) 地对 (sum[node]) 进行维护:

    找规律:我们可以将第 (k) 个和第 (k+1) 个看成一组,它们的贡献之和为 (x) ,那么所有组的贡献总和为:(x*) 组数 :

    但是可能还会存在一个落单的,别忘了计算这个落单的贡献。

    对于 (lazy1[node]) 的维护,我们直接令 (lazy1[node]+=k) 即可 。

    这样就可以了嘛?真 · 线段树板子?上述做法有什么问题嘛?

    (Problem1:) 正负号问题 。

    一个很重要的问题:我们上面的操作都是基于第一个数是 (-) 才行,也就是说,我们的操作形式要和题目中给出的操作 (1) 的形式一直才对 。

    题目中的操作 (1) 不是以 (-) 的形式开始的嘛?为什么当前区间第一个数可能会是 (+) 的形式?

    如上图所示:如果你要对 ([1,5]) 进行操作 (1) ,当你递归到你的右儿子 ([4,5]) 的时候发现是以 (+) 开头的,但是由于左儿子的最左端就是它父亲的最左端,所以左儿子开头的正负性和父亲开头的正负性是相等的,我们比较好判断,我们重点要判断右儿子的开头的正负性 。

    想一想哈,如果左儿子的区间长度为 (2k),那么右儿子开头的正负性和父亲的相同;如果左儿子的区间长度为 (2k+1),那么右儿子开头的正负性和父亲的相反 。

    (Problem2:) 标记下传问题

    注意一下我们 (lazy1[node]=k) 的定义:表示线段树中 (node) 节点所代表的区间进行了一次操作 (1)

    注意到操作 (1) 是有两个性质的:

    ①. 正负号交替;②. 每一项的绝对值之差为首项的绝对值。

    注意到上图右儿子中,操作 (1) 的首项为 (+4x) ,而每一项的绝对值之差为 (x),不符合操作 (1) 的定义了,所以我们不能直接维护懒标记。

    提取公因式:我们将 (+4x) 看作是 (+3x+x),将 (-5x) 看作是 (-3x-2x),注意到右边的 (+x)(-2x) 了没,这不就是操作 (1) 嘛?对于左边的 (+3x)(-3x) 呢,我们可以看出一种新的操作:

    (3.l,r,y) 表示将 ([l,r]) 的第 (l) 个数减 (y),第 (l+1) 个数加 (y),第 (l+2) 个数减 (y) ......

    即:令 (a_l=a_l-y)(a_{l+1}=a_{l+1}+y)(a_{l+2}=a_{l+2}-y) ...... (a_r=a_r+(-1)^{r-l+1}y)

    所以对于上面的右儿子,我们可以看作是先进行了一次 (x=-x) 的操作 (1) ,再加上一次 (y=-y) 的操作 (3)

    对于操作 (3),我们同样开一个懒标记 (lazy2[node]=k) 来进行维护,有 (1) 必有 (2)(qwq)

    (insert)

    void insert(int node,int l,int r,int x,int y,int k)
    {
    	if(x<=l&&r<=y)       //[l,r]被完全覆盖 
    	{
    		int len=r-l+1;   //当前区间的长度 
    		int d=l-x;       //x~l-1之间有多少个数 
    		if(d&1)          //如果l前面有有奇数个数,说明第l个数在[x,y]内是第偶数个数,那么应该是从加号开始 
    		{
    			lazy1[node]-=k;  //由于我们lazy1的设定是从减号开始的,所以这里应该是-= 
    			lazy2[node]-=k*d;
    			   //将操作1进行拆解,所提取出来的公因数的绝对值为k*d,同样我们操作3的设定也是从减号开始,所以这里还是-= 
    			
    			sum[node]-=(len/2)*k;  //对sum[node]进行维护,将它们两两看成一组,一共有len/2组,每一组的贡献为-k 
    			if(len&1) sum[node]+=k*(r-x+1); 
    			   //如果区间长度为奇数,说明两两一组有余,由于我们已经判断出该区间是从加号开始的,所以这个余出的数一定是加 
    		}
    		else             //从减开始,与上面的同理,只是符号都改成相反的而已 
    		{
    			lazy1[node]+=k;
    			lazy2[node]+=k*d;
    			sum[node]+=(len/2)*k;
    			if(len&1) sum[node]-=k*(r-x+1);
    		}
    		return ;
    	}
    	pushdown(node,l,r);  //记得标记下传   
    	int mid=(l+r)>>1;
    	if(x<=mid) insert(node<<1,l,mid,x,y,k);
    	if(y>mid) insert(node<<1|1,mid+1,r,x,y,k);
    	update(node);        //维护父亲节点的sum 
    }
    

    (pushdown)

    如果你已经理解了 (insert) 的思路,那么 (pushdown) 的思路也就不难了。

    void pushdown(int node,int l,int r)
    {
    	int mid=(l+r)>>1;
    	int len=r-l+1;
    	int len1=mid-l+1;
    	int len2=r-mid;
    	if(lazy1[node])                           //时刻牢记lazy1是从减开始的 
    	{
    		sum[node<<1]+=(len1/2)*lazy1[node];   //两两分组算贡献 
    		if(len1&1) sum[node<<1]-=len1*lazy1[node];  //两两分组有余,那么余的这个数肯定是减的 
    		lazy1[node<<1]+=lazy1[node];          //维护左儿子的lazy1 
    		if(len1&1)                            //如果左儿子的区间长度为奇数,说明右儿子的左端点是第偶数个数,则是从加开始 
    		{
    			lazy1[node<<1|1]-=lazy1[node];    //由于是从加开始,所以这里是减 
    			lazy2[node<<1|1]-=lazy1[node]*len1; 
    			   //只有右儿子不满足操作1的性质②,因此我们要拆分操作,所以我们只维护右儿子的lazy2 
    			sum[node<<1|1]-=(len2/2)*lazy1[node]; //两两分组算贡献 
    			if(len2&1) sum[node<<1|1]+=lazy1[node]*len; //两两分组有余,由于右儿子是从加开始的,所以余的这个数肯定是加的 
    		} 
    		else                                  //右儿子的左端点从减开始,除了符号和上面都一样 
    		{
    			lazy1[node<<1|1]+=lazy1[node];
    			lazy2[node<<1|1]+=lazy1[node]*len1;
    			sum[node<<1|1]+=(len2/2)*lazy1[node];
    			if(len2&1) sum[node<<1|1]-=lazy1[node]*len;
    		}
    		lazy1[node]=0;                        //别忘了清空标记1 
    	}
    	if(lazy2[node])                           //下传标记2 
    	{
    		if(len1&1) sum[node<<1]-=lazy2[node]; //对于操作3,发现两两算贡献的时候可以抵消,所以如果有余肯定是减的 
    		lazy2[node<<1]+=lazy2[node];          //维护左儿子的lazy2 
    		if(len1&1)                            //右儿子的左端点从加开始    
    		{
    			lazy2[node<<1|1]-=lazy2[node];    //维护右儿子的lazy2,由于右儿子的左端点是从加开始的,所以这里是减 
    			if(len2&1) sum[node<<1|1]+=lazy2[node];
    		} 
    		else                                  //右儿子的左端点从减开始,仍然只有符号不同 
    		{
    			lazy2[node<<1|1]+=lazy2[node];
    			if(len2&1) sum[node<<1|1]-=lazy2[node];
    		}
    		lazy2[node]=0;                        //清空lazy2 
    	}
    }
    

    完整版(Code:)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int read()
    {
    	char ch=getchar();
    	int a=0,x=1;
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') x=-x;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		a=(a<<1)+(a<<3)+(ch-'0');
    		ch=getchar();
    	}
    	return a*x;
    }
    const int N=1e5+5;
    int n,m;
    int a[N],sum[N<<2],lazy1[N<<2],lazy2[N<<2];
    void update(int node)
    {
    	sum[node]=sum[node<<1]+sum[node<<1|1];
    }
    void pushdown(int node,int l,int r)
    {
    	int mid=(l+r)>>1;
    	int len=r-l+1;
    	int len1=mid-l+1;
    	int len2=r-mid;
    	if(lazy1[node])                           //时刻牢记lazy1是从减开始的 
    	{
    		sum[node<<1]+=(len1/2)*lazy1[node];   //两两分组算贡献 
    		if(len1&1) sum[node<<1]-=len1*lazy1[node];  //两两分组有余,那么余的这个数肯定是减的 
    		lazy1[node<<1]+=lazy1[node];          //维护左儿子的lazy1 
    		if(len1&1)                            //如果左儿子的区间长度为奇数,说明右儿子的左端点是第偶数个数,则是从加开始 
    		{
    			lazy1[node<<1|1]-=lazy1[node];    //由于是从加开始,所以这里是减 
    			lazy2[node<<1|1]-=lazy1[node]*len1; 
    			   //只有右儿子不满足操作1的性质②,因此我们要拆分操作,所以我们只维护右儿子的lazy2 
    			sum[node<<1|1]-=(len2/2)*lazy1[node]; //两两分组算贡献 
    			if(len2&1) sum[node<<1|1]+=lazy1[node]*len; //两两分组有余,由于右儿子是从加开始的,所以余的这个数肯定是加的 
    		} 
    		else                                  //右儿子的左端点从减开始,除了符号和上面都一样 
    		{
    			lazy1[node<<1|1]+=lazy1[node];
    			lazy2[node<<1|1]+=lazy1[node]*len1;
    			sum[node<<1|1]+=(len2/2)*lazy1[node];
    			if(len2&1) sum[node<<1|1]-=lazy1[node]*len;
    		}
    		lazy1[node]=0;                        //别忘了清空标记1 
    	}
    	if(lazy2[node])                           //下传标记2 
    	{
    		if(len1&1) sum[node<<1]-=lazy2[node]; //对于操作3,发现两两算贡献的时候可以抵消,所以如果有余肯定是减的 
    		lazy2[node<<1]+=lazy2[node];          //维护左儿子的lazy2 
    		if(len1&1)                            //右儿子的左端点从加开始    
    		{
    			lazy2[node<<1|1]-=lazy2[node];    //维护右儿子的lazy2,由于右儿子的左端点是从加开始的,所以这里是减 
    			if(len2&1) sum[node<<1|1]+=lazy2[node];
    		} 
    		else                                  //右儿子的左端点从减开始,仍然只有符号不同 
    		{
    			lazy2[node<<1|1]+=lazy2[node];
    			if(len2&1) sum[node<<1|1]-=lazy2[node];
    		}
    		lazy2[node]=0;                        //清空lazy2 
    	}
    }
    void build(int node,int l,int r)
    {
    	if(l==r) 
    	{
    		sum[node]=a[l];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(node<<1,l,mid);
    	build(node<<1|1,mid+1,r);
    	update(node);
    }
    void insert(int node,int l,int r,int x,int y,int k)
    {
    	if(x<=l&&r<=y)       //[l,r]被完全覆盖 
    	{
    		int len=r-l+1;   //当前区间的长度 
    		int d=l-x;       //x~l-1之间有多少个数 
    		if(d&1)          //如果l前面有有奇数个数,说明第l个数在[x,y]内是第偶数个数,那么应该是从加号开始 
    		{
    			lazy1[node]-=k;  //由于我们lazy1的设定是从减号开始的,所以这里应该是-= 
    			lazy2[node]-=k*d;
    			   //将操作1进行拆解,所提取出来的公因数的绝对值为k*d,同样我们操作3的设定也是从减号开始,所以这里还是-= 
    			sum[node]-=(len/2)*k;  //对sum[node]进行维护,将它们两两看成一组,一共有len/2组,每一组的贡献为-k 
    			if(len&1) sum[node]+=k*(r-x+1); 
    			   //如果区间长度为奇数,说明两两一组有余,由于我们已经判断出该区间是从加号开始的,所以这个余出的数一定是加 
    		}
    		else             //从减开始,与上面的同理,只是符号都改成相反的而已 
    		{
    			lazy1[node]+=k;
    			lazy2[node]+=k*d;
    			sum[node]+=(len/2)*k;
    			if(len&1) sum[node]-=k*(r-x+1);
    		}
    		return ;
    	}
    	pushdown(node,l,r);  //记得标记下传   
    	int mid=(l+r)>>1;
    	if(x<=mid) insert(node<<1,l,mid,x,y,k);
    	if(y>mid) insert(node<<1|1,mid+1,r,x,y,k);
    	update(node);        //维护父亲节点的sum 
    }
    int query(int node,int l,int r,int x,int y)
    {
    	if(x<=l&&r<=y) return sum[node];
    	pushdown(node,l,r);
    	int mid=(l+r)>>1;
    	int cnt=0;
    	if(x<=mid) cnt+=query(node<<1,l,mid,x,y);
    	if(y>mid) cnt+=query(node<<1|1,mid+1,r,x,y);
    	return cnt; 
    }
    int main()
    {
    	//freopen("country.in","r",stdin);
    	//freopen("country.out","w",stdout); 
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    	    a[i]=read();
    	build(1,1,n);
    	for(int i=1;i<=m;i++)
    	{
    		int opt,l,r,x;
    		opt=read();
    		l=read();r=read();
    		if(l>r) swap(l,r);
    		if(opt==1) 
    		{
    			x=read();
    			insert(1,1,n,l,r,x);
    		}
    		else printf("%d
    ",query(1,1,n,l,r));
    	}
    	return 0;
    } 
    

    (The) (last)

    线段树是一种很优秀的数据结构,希望你们能多加练习,体会它的魅力。

    本着出于让你们练一练线段树的目的,我才出的这道题,没想到正解还真的不好做。出题人埋头苦思想了 (2.5h) 才想出正解,当然可能有更优的方法等待着你们来挖掘。

    最后膜一下你们这群 (Θ(n^2))(1e4) 数据的卡常带师。

    ​ ——暗ざ之殇

    T2

    讲道理,凭良心说话,T2要比T1简单亿

    传送门:https://www.luogu.com.cn/problem/U121369?contestId=31441

    (30pts)

    **首先感谢学长不杀之恩,看到数据范围,了解到一个(30pts)的做法,因为(n=1),所以输出(0)即可

    (100pts)

    题目描述的已经很清楚了,把问题抽象化出来,就是求节点(1)的单源最短路,再求所有节点到节点(1)的单源最短路,累加答案即可

    首先,我们看到,求所有到(1),求(1)到所有第一个想到的解法应该是(floyd)

    这个算法可以在(O(n^3))的时间内求出全源最短路,然后我们调用我们需要的数据,累加答案即可

    但是显然这不是正解,因为:

    对于(100)%的数据,有(n<=1000)

    (1000^3)你玩呢?所以我又想出了如下方案:

    1、跑(n)遍堆优化的(dijkstra),然鹅,复杂度(O(n*(m+n)logn)),并没有什么实质上的优化

    2、(n)(spfa),最坏复杂度(O(n^2*m)),依然是我们无法接受的量级

    暂时没有思路,所以我在小本子上把样例画了出来,发现了这样一个有趣的现象:

    (原谅我是灵魂画手)

    我们发现,如上图(假设边权都是(1)),我们跑一遍(1)->(4)的最短路,是(1)->(3)->(4),如果我们把边反着建,再跑一边最短路,就是(1)->(4),也就是正向图(4)->(1)的最短路

    所以我直接乱搞一波反向建边+两遍(dijkstra)(反正是乐多赛制可以实时观测评测结果)

    直接交上去发现,真就切掉了。。。

    (AC) (Code)(后来我发现洛谷上还有一个几乎一模一样的题P1629,传送门:https://www.luogu.com.cn/problem/P1629

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #define N 1010
    using namespace std;
    typedef long long int ll;
    priority_queue< pair<int,int> >q;
    ll mapp[N][N],dis[N],vis[N],cop[N],n,m,d,ans;
    void upside_down(){
    	for(int i=1;i<=n;i++)
    		for(int j=i+1;j<=n;j++)
    			swap(mapp[i][j],mapp[j][i]);//这里是反向建边(我比较懒直接用的邻接矩阵
    }
    void dijkstra(){
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[1]=0;
    	q.push(make_pair(0,1));
    	while(q.size()!=0){	
    		int x=q.top().second;
    		q.pop();
    		vis[x]=1;
    		for(int y=1;y<=n;y++){
    			int z=mapp[x][y];
    			if(dis[y]>dis[x]+z&&vis[y]!=1){
    				dis[y]=dis[x]+z;
    				q.push(make_pair(-dis[y],y)); 
    			}
    		}
    	} 
    }
    int main(){
    	scanf("%lld%lld%lld",&n,&m,&d);
    	memset(mapp,0x7f,sizeof(mapp));
    	for(ll i=0;i<m;i++){
    		ll x,y,z;
    		scanf("%lld%lld%lld",&x,&y,&z);
    		mapp[x][y]=min(z,mapp[x][y]);
    	}
    	for(int i=1;i<=n;i++)
    		mapp[i][i]=0;
    	dijkstra();
    	for(int i=1;i<=n;i++)
    		ans+=dis[i];
    	upside_down();
    	dijkstra();
    	for(int i=1;i<=n;i++)
    		ans+=dis[i];
    	printf("%lld",ans+(n-1)*d*2);
    	return 0;
    }
    

    T3

    传送门:https://www.luogu.com.cn/problem/U121284?contestId=31441

    又双叒叕是一个图论题!

    (50pts)

    仍然先感谢学长不杀之恩:对于(50)%的数据,(n)<=100,所以我如果没猜错的话,这(50pts)应该是给了暴搜吧……但是好像机房里没有人愿意拿这个部分分唉?

    (100pts)

    扫一眼题面,我们了解到,本题要求出从(s)->(e)的一条路径,使得这条路径上,最大的边权最小,并输出这个最大边权最小值

    想了一通有没有这么一个算法来应对这种问题,,,但是显然没有

    突然想到,本题的答案似乎具有单调性,可以使用二分法求解

    具体怎么个单调性?

    首先,我们二分一个中点(Mid),然后在图上跑一遍,如果遇到权值比(Mid)值要大的边,就自动忽视掉

    然后,看看剩下的这些边,能不能支持我们从(s)->(e)

    如果不走比(Mid)权值大的边,可以支持我们从(s)->(e),说明那个最大边权最小值比(Mid)小,反之,比(Mid)

    然而还有一个问题,我们怎么判断这两个点在上述二分求解的情况中是否被联通呢?

    一开始我想到了(dfs),但是经过我多年写暴搜(TLE)的惨痛教训,我真的不愿意写这个搜索了。。。

    于是我又想起了一个玄学做法——跑(dijkstra),首先赋值(dis)数组为(0x7f7f7f7f),从(s)跑一遍(dijkstra),看看(e)的最短路有没有被更新,如果被更新了,说明(s),(e)联通,反之,(s)(e)不连通

    然后我把这两个玄学的思路结合到了代码里:

    (Code)

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<vector>
    #include<queue>
    using namespace std;
    const int N=5005;
    typedef long long int ll;
    struct edge{
    	int to,cost;
    };
    vector<edge>v[N];
    priority_queue< pair<int,int> >q;
    ll dis[N],vis[N],cop[N],n,m,s,e;
    bool dijkstra(int a,int m){
    	memset(dis,0x7f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[a]=0;
    	q.push(make_pair(0,a));
    	while(q.size()!=0){
    		int x=q.top().second;
    		q.pop();
    		for(unsigned int i=0;i<v[x].size();i++){
    			int y=v[x][i].to,z=v[x][i].cost;
    			if(z>m) continue;
    			if(dis[y]>dis[x]+z){
    				dis[y]=dis[x]+z;
    				q.push(make_pair(-dis[y],y));
    			}
    		}
    	}
    	if(dis[e]>2147483646) return false;
    	else return true;
    }
    int main(){
    	scanf("%lld%lld",&n,&m);
    	for(ll i=0,x,y,z;i<m;i++){
    		scanf("%lld%lld%lld",&x,&y,&z);
    		v[x].push_back((edge){y,z});
    		v[y].push_back((edge){x,z});
    	}
    	scanf("%lld%lld",&s,&e);
    	ll l=0,r=1000000,ans=1000000;
    	while(l<=r){
    		ll Mid=(l+r)>>1;
    		if(dijkstra(s,Mid)==true){
    			r=Mid-1;
    			ans=min(ans,Mid);
    		}else l=Mid+1;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    结果(AC)了,比赛后学长发了一下题解,正解是用(Kruscal)最小生成树算法做的,然后因为我不会(Kruscal)所以这里直接贴一波(std)叭!

    (std) (Code)

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cctype>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    inline int read()
    {
        int a=0,b=1;
        char c=getchar();
        while(!isdigit(c))
        {
            if(c=='-')
                b=-1;
            c=getchar();
        }
        while(isdigit(c))
        {
            a=(a<<3)+(a<<1)+(c^48);
            c=getchar();
        }
        return a*b;
    }
    int num,s,t,maxn,father[5005];
    struct edge
    {
        int from,to,dis;
    }e[400005];
    bool cmp(edge a,edge b)
    {
        return a.dis<b.dis;
    }
    int getfather(int x)
    {
        if(father[x]==x)
            return x;
        father[x]=getfather(father[x]);
        return father[x];
    }
    int n,m,cnt;
    long long ans;
    int main()
    {
    //  freopen("486.in","r",stdin);
    //  freopen("486.out","w",stdout);
        int n=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            e[i].from=read(),e[i].to=read(),e[i].dis=read();
        }
        sort(e+1,e+m+1,cmp);
        s=read(),t=read();
        for(int i=1;i<=n;i++)
        {
            father[i]=i;
        }
        for(int i=1;i<=m&&cnt<=n-1;i++)
        {
            int fu=getfather(e[i].from),fv=getfather(e[i].to);
            if(fu==fv)
                continue;
            father[fu]=fv;
            cnt++;
            ans+=e[i].dis;
            if(getfather(s)==getfather(t))
            {
                printf("%d
    ",e[i].dis);
                return 0;
            }
        }
        return 0;
    } 
    

    ##其实有T4,但是是一个毒瘤高精+动规,所以劳资不写啦!!!

  • 相关阅读:
    交流课件
    ARC127F ±AB
    CF1566F xor-quiz
    JOISC 2016 Day 1 棋盘游戏
    dev分支和release是什么
    drf 笔记
    drf 序列化的写法 总结
    Linux系统编程
    C/C++ 网络编程
    C++提高编程
  • 原文地址:https://www.cnblogs.com/zaza-zt/p/13293947.html
Copyright © 2011-2022 走看看