问题标题: 酷町堂:LCA或MST求解6412

0
0
已解决
赵逸凡
赵逸凡
初级启示者
初级启示者
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n,m,fa[100000];
bool map[100000];
struct node{
	int to,en,w;
	bool operator>(const node& tmp)const{
		return w>tmp.w;
	}	
};
priority_queue<node, vector<node>, greater<node> > p,q,tmp;

void init()
{
	for(int i=1;i<=n;i++)
		if(map[i])
			fa[i]=i;
}
int find(int x)
{
	if(fa[x]==x)
		return x;
	return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y)
		return ;
	fa[x]=y;
}
bool same(int x,int y)
{
	if(find(x)==find(y))
		return true;
	return false;
}
int kruscal()
{
	int MST=0;
	init();
	while(!q.empty())
	{
		node u=q.top();
		tmp.push(u);
		q.pop();
		if(!same(u.en,u.to))	
		{
			unite(u.en,u.to);
			MST+=u.w;
		}
	}
	while(!tmp.empty())
	{
		q.push(tmp.top());
		tmp.pop();
	}
	return MST;
} 

int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		p.push((node){x,y,z});
	}
	scanf("%d",&m);
	while(m--)
	{
		char opt;
		int x;
		cin>>opt;
		if(opt=='+')
		{
			cin>>x;
			map[x]=true;
			while(!p.empty())
			{
				node h=p.top();
				tmp.push(h);
				p.pop();
				if(h.en==x||h.to==x)
					q.push(h);
			}
			while(!tmp.empty())
			{
				p.push(tmp.top());
				tmp.pop();
			}
		}
		if(opt=='-')
		{
			scanf("%d",&x);
			map[x]=false;
			while(!q.empty())
			{
				node h=q.top();
				if(h.en!=x&&h.to!=x)
					tmp.push(h);
				q.pop();
			}
			while(!tmp.empty())
			{
				q.push(tmp.top());
				tmp.pop();
			}
		}
		if(opt=='?')
			printf("%d\n",kruscal());
	}
	return 0;
}

我先写了个Kruskal求动态MST思路,先发现读入字符问题,cin处理之后对拍样例,发现第一个输出的是6,而样例的是5,也显然是5.

后来发现Kruskal不能处理虚点(即不需要连接的点,但与要连接的点有关联),或者说要处理的MST不是真的MST

哪位Dalao帮我用Prim改写下这道题的Kruskal啊,或者帮我改下init的函数,因为我发现一定是init出了问题,但这涉及到Kruskal的本质,目前想不出来惹。

我的思路是如果是“+"就把接下来输入的这个点有直连的边加入要求的MST图中,如果是"-"就把输入的这个点有关的直连边删去,但好像有点问题。如果是"?"就求加入q队列的要求的MST图中。

当然我知道这道题正解是LCA,MST不加STL_map一定会TLE。如果能给出LCA思路更好。

赵逸凡在2021-05-16 19:01:40追加了内容

     


0
已采纳
飞速小程序开发商
飞速小程序开发商
初级守护
初级守护

大佬牛逼啊~

都学算法高级了!

LCA的思路(会超时):

首先写一个模板,注意,要存一下最小公共祖先的位置

“+”和“-”都很简单,我就不说明了

查询时的操作如图所示:

点1,2,3,4。。。

(1,2)的最小公共祖先,3,4.。。。

((1,2)的最小公共祖先,3 )的最小公共祖先,4.。。。

 

 

 

这个代码显然会超时,但至少结果按理来说是对的,可以优化一下

 

 

————————————————————————

PS:大佬勿喷!

0
0
0
0
赵逸凡
赵逸凡
初级启示者
初级启示者

能给出思路者,悬赏最高1000酷町豆,如果思路非常好悬赏4000酷町豆或以上。

0
谭迪元
谭迪元
资深光能
资深光能

1.相邻两个节点的路径一定在连接所有标记的点的最短路径中 , 这很好证明 , 在树上 , 两个点之间的路径是唯一的 , 连接所有标记的点的最短路径必然包括 dfs 序相邻两个点的路径

2.连接所有标记的点的最短路径一定都被连接相邻两个节点的路径所包含 , 这也很好证明 , 连接相邻两个节点的路径已经将这些标记点都联通了 , 既然是连接所有标记的点的最短路径 , 一定不会比连接相邻两个节点的路径再多

由上可知 , 所有连接相邻两个节点的路径必然是连接所有标记的点的最短路径 , 且包含了所有路径 , 并且可以把它看做是从 dfs 序出发 , 遍历树上其它标记点 , 再回到起点 , 由树的性质可知 , 每条路径一定走过两遍 , 因此 , 相邻两个节点的距离之和即为询问答案的两倍 , 得证

有了这个结论以后 , 之后就好办了 , 可以用平衡树或 set 之类的进行维护 , 如果插入一个点 xixi , 那么找到它相邻的两个点 lili , riri , 询问答案的两倍 resres 就加上 dist(li,xi)+dist(xi,ri)−dist(li,ri)dist(li,xi)+dist(xi,ri)−dist(li,ri) , 如果删除一个点 xixi , 那么找到它相邻的两个点 lili , riri , 询问答案的两倍 resres 就减去 dist(li,xi)+dist(xi,ri)−dist(li,ri)dist(li,xi)+dist(xi,ri)−dist(li,ri) , 如果进行询问 , 那么就输出 res/2res/2

时间复杂度 : O(mlogn)

谭迪元在2021-05-30 14:00:26追加了内容

@赵逸凡 望采纳!!!

我要回答