【福建集训】最小生成树(2/3)

A.最小花费

一个模型题,怎么把区间查询推断转移到MST上来。首先观察到题上让我们求总和最小,考虑到将这个题转化为图论种最短路或最小生成树上。这个题中我们会发现如果想知道每个数的值其实就是知道单点奇偶性,但我们每次只能获得一段区间和的奇偶性,考虑转化为前缀和思想。如果知道区间[l,r]的和的奇偶性,实际上是知道了sum[r]-sum[l-1],从这一步我们思考假如又知道了[k,r](k=i+1)的和的奇偶性,实际上我们可以通过代换得到sum[k]-sum[i]的值。依次类推我们发现如果认为每次查询[l,r]的和的奇偶性是在(l-1)->r连了一条权值为查询消耗的边的话,那么当我们得到每个点的奇偶性时我们一定建了一张让全部点连通的图。进一步思考,每个点实际上只需要被连接一次就可以了,整个图的最小边权和的形态应该是MST。

打个模板就可以了

#include<bits/stdc++.h>
using namespace std;
#define re register
#define fo1(l,r) for(re int i=l;i<=r;++i)
#define fo2(l,r) for(re int j=l;j<=r;++j)
#define fo3(l,r) for(re int k=l;k<=r;++k)
#define fo4(l,r) for(re int tt=l;tt<=r;++tt)
#define fo(l) for(re int i=h[l],go;i;i=x[i].last)
#define inf 0x3f3f3f3f
#define INF 0x7fffffffffffffff
#define LL long long
#define itn int
#define DB double
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+ch-48;
        ch=getchar();
    }
    return x*f;
}
const int N=2010;
int minn[N];int v[N][N];bitset<N> pd;
int main()
{
    freopen("cost.in","r",stdin);freopen("cost.out","w",stdout);
    int n=read();
    fo1(1,n)
    {
        fo2(1,n-i+1)
        {
            int l=i-1,r=j+i-1,ls=read();
            v[l][r]=ls;v[r][l]=ls;
        }
    }
    memset(minn,127,sizeof(minn));
    minn[0]=0;LL ANS=0;int MINN,bh;
    fo1(0,n)
    {
        MINN=inf;
        fo2(0,n)
            if(pd[j]==0)
                if(minn[j]<MINN)MINN=minn[j],bh=j;
        ANS+=(LL)minn[bh];
        fo2(0,n)
            if(pd[j]==0 && v[bh][j]<minn[j])minn[j]=v[bh][j];
        pd[bh]=1;
    }
    printf("%lld",ANS);
    fclose(stdin);fclose(stdout);
    return 0;
}

B.路径统计

本题是Kruskal重构树的完全模板。

先介绍树的建立过程:

①与Kruskal算法类似,先按边权排序所有边,然后从小到大遍历。每个点初始是一个集合(并查集),同时也是树上一个权值0的散点。

②对于一条边(x,y)如果x与y在一个集合那么不做任何处理,如果在不同集合则合并,同时在树上将x与y集合的根与一个新节点z分别建边,同时把z的点权设置为边权w

再介绍重构树的性质:

对于原图中的任意两个节点x与y,它们之间的所有路径上的边权最大值的最小值等于Kruskal重构树中二者LCA点的权值。

(这条性质是显然的,因为显然x与y二者的LCA值是能使二者连通的边的最小权值的边)

#include<bits/stdc++.h>
using namespace std;
#define re register
#define fo1(l,r) for(re int i=l;i<=r;++i)
#define fo2(l,r) for(re int j=l;j<=r;++j)
#define fo3(l,r) for(re int k=l;k<=r;++k)
#define fo4(l,r) for(re int tt=l;tt<=r;++tt)
#define fo(l) for(re int i=h[l],go;i;i=x[i].last)
#define inf 0x3f3f3f3f
#define INF 0x7fffffffffffffff
#define LL long long
#define itn int
#define DB double
inline LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(!isdigit(ch))
	{
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
    while(isdigit(ch))
	{
		x=(x<<3)+(x<<1)+ch-48;
		ch=getchar();
	}
    return x*f;
}
const int N=1e5+10,M=2e5+10;
struct node
{
	int from,to,dis;
}x[M];
itn ss;
inline bool cmp(node xx,node yy)
{
	return xx.dis<yy.dis;
}
int fa[N*2],num,siz[N*2];
inline int yget(int xx)
{
	if(fa[xx]==xx)
		return xx;
	return fa[xx]=yget(fa[xx]);
}
int main()
{
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	itn n=read(),m=read();
	LL k=read();
	fo1(1,2*n)
		fa[i]=i;
	fo1(1,n)
		siz[i]=1;
	fo1(1,m)
	{
		x[++ss].from=read();
		x[ss].to=read();
		x[ss].dis=read();
	}
	sort(x+1,x+1+m,cmp);
	num=n;
	fo1(1,m)
	{
		int xx=x[i].from,yy=x[i].to,w=x[i].dis;
		int ls1=yget(xx),ls2=yget(yy);
		if(ls1!=ls2)
		{
			LL ls3=(LL)siz[ls1];
			LL ls4=(LL)siz[ls2];
			LL lss=ls3*ls4;
			k-=lss;
			if(k<=0)
			{
				printf("%d",w);
				return 0;
			}
			++num;
			fa[ls1]=num;
			fa[ls2]=num;
			siz[num]=siz[ls1]+siz[ls2];
//			cout<<ls1<<" "<<ls2<<"合成"<<num<<" "<<siz[num]<<endl;
		}
	}
	printf("%d",x[m].dis);//可能会有散点? 
	fclose(stdin);
	fclose(stdout);
	return 0;
}

  

posted @ 2023-07-16 21:37  小鱼儿吼吼  阅读(26)  评论(0)    收藏  举报