• [冲刺国赛2022] 最小生成树


    一、题目

    有编号为 \(0,1,2...n\)\(n+1\) 个点,一共有两类边:

    • \(n\) 条边,第 \(i\) 条连接 \((0,i)\),边权为 \(a_i\)
    • \(m\) 条边,在 \((u,v)\) 之间连一条边权为 \(w\) 的边。

    \(q\) 次修改,每次将 \(a_x\) 修改为 \(y\),每次修改之后求出最小生成树,注意修改不独立

    \(n,m,q\leq 3\cdot 10^5\)

    二、解法

    线段树分治加 link-cut-tree 是我觉得最垃圾的做法,毫无美感可言,所以也没给什么分。

    首先考虑如果第二类边是链怎么做?直接对链建立线段树,对于每个区间 \([l,r]\),我们只需要维护 t[0/1][0/1] 表示考虑区间中的所有一类边和二类边,\(l\) 的连通块是否与 \(0\) 连通;\(r\) 的连通块是否与 \(0\) 连通;区间内的其它连通块都已经与 \(0\) 连通。达到这个目的需要花费的最小代价。

    在合并时,需要处理中间两个连通块。如果都不与 \(0\) 连通,那么不合法;如果都与 \(0\) 连通,不需要花费代价就可以合并;如果只有一边与 \(0\) 连通,那么需要花费中间那条二类边的代价。

    进一步解释上面的方法,考虑 \(l,r\) 的连通块具体是什么样子不需要关心,只需要关心与 \(0\) 的连通性就可以支持合并,因为中间那条边的权值都是一样的。另外对于叶子节点的初始化,只有 t[1][1] 需要设置为 \(a_i\),其他都设置成 \(0\) 就行了,正确性不难理解。

    \(\tt UPD\):上面的方法也可以理解成,设 \(dp_{i,0/1}\) 表示考虑前 \(i\) 个点,\(i\) 的连通块和 \(0\) 不连通 \(/\) 连通。那么可以直接动态 \(dp\)

    推广到普遍的情况,考虑找出第二类边的等效链。我们对第二类边单独跑 kruskal,对于每个连通块都维护其对应的等效链,合并两个连通块的时候,我们也合并对应的等效链,直接把两条链的端点用这条边接起来就行了。

    这样做为什么是对的呢?考虑出错的情形是:这条边原来连接 \((u,v)\),但在等效链上连接了 \((x,y)\),如果 \((u,x)/(v,y)\) 不在同一个连通块中就可能出错。但是考虑第一类边时,如果这条二类边起作用,那么根据 kruskal 的过程,这条边的作用环境仍然是不变的(也就是多考虑了一些边,这条边起作用时必定有 \((u,x)\)\((v,y)\) 都连通的性质)

    那么跑完 kruskal 之后直接上线段树维护,时间复杂度 \(O(n\log n)\)

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int M = 300005;
    const int inf = 0x3f3f3f3f;
    #define ll long long
    #define pb push_back
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    void write(ll x)
    {
    	if(x>=10) write(x/10);
    	putchar(x%10+'0');
    }
    int n,m,q,a[M],b[M],fa[M],p[M],id[M];
    vector<int> s[M],w[M];ll t[M<<2][2][2];
    struct node{int u,v,c;}e[M];
    int find(int x)
    {
    	if(x!=fa[x]) fa[x]=find(fa[x]);
    	return fa[x];
    }
    void merge(int u,int v,int c)
    {
    	u=find(u);v=find(v);if(u==v) return ;
    	if(s[u].size()<s[v].size()) swap(u,v);
    	fa[v]=u;for(int x:s[v]) s[u].pb(x);
    	w[u].pb(c);for(int x:w[v]) w[u].pb(x);
    }
    void up(int i,int zxy)
    {
    	int l=i<<1,r=i<<1|1;
    	memset(t[i],0x3f,sizeof t[i]);
    	for(int a=0;a<2;a++) for(int b=0;b<2;b++)
    		for(int c=0;c<2;c++) if(b|c) for(int d=0;d<2;d++)
    			t[i][a][d]=min(t[i][a][d],t[l][a][b]
    			+t[r][c][d]+((b&c)?0:zxy));
    }
    void build(int i,int l,int r)
    {
    	if(l==r)
    	{
    		for(int j=0;j<2;j++)
    			for(int k=0;k<2;k++)
    				t[i][j][k]=(j&k)?a[p[l]]:0;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    	up(i,b[mid]);
    }
    void ins(int i,int l,int r,int x)
    {
    	if(l==r)
    	{
    		for(int j=0;j<2;j++)
    			for(int k=0;k<2;k++)
    				t[i][j][k]=(j&k)?a[p[l]]:0;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(mid>=x) ins(i<<1,l,mid,x);
    	else ins(i<<1|1,mid+1,r,x);
    	up(i,b[mid]);
    }
    signed main()
    {
    	freopen("mst.in","r",stdin);
    	freopen("mst.out","w",stdout);
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),fa[i]=i,s[i].pb(i);
    	for(int i=1;i<=m;i++)
    		e[i].u=read(),e[i].v=read(),e[i].c=read();
    	sort(e+1,e+1+m,[&](node a,node b){return a.c<b.c;});
    	for(int i=1;i<=m;i++)
    		merge(e[i].u,e[i].v,e[i].c);
    	for(int i=1;i<n;i++)
    		merge(i,i+1,inf);
    	int rt=find(1),cnt=0;
    	for(int x:s[rt]) p[++cnt]=x;
    	cnt=0;for(int x:w[rt]) b[++cnt]=x;
    	for(int i=1;i<=n;i++) id[p[i]]=i;
    	build(1,1,n);q=read();
    	while(q--)
    	{
    		int x=read(),y=read();a[x]=y;
    		ins(1,1,n,id[x]);
    		write(t[1][1][1]);puts("");
    	}
    }
    
  • 相关阅读:
    Spring工厂方法(factory-bean)配置bean
    subline关联linux系统
    第五篇 scrapy安装及目录结构,启动spider项目
    第八篇 编写spider爬取jobbole的所有文章
    第七篇 css选择器实现字段解析
    第六篇 xpath的用法
    爬虫 主要基础知识
    在ubuntu16下安装virtualenv+virtualenvwrapper
    git 和github简介
    git stash封存分支 以及关于开发新功能的处理
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16496097.html
Copyright © 2020-2023  润新知