洛谷P1197星球大战-链式前向星及其简单运用

前向星

在了解链式前向星之前,先简单了解下前向星。前向星是一种边集数组,先把每条边按照从小到大的顺序排序,如果起点一样,那么就按照终点从小到大来排序,并记录下每个点为起点在数组中的位置和该点所连边的数量。

  • (len[i])表示以(i)为起点的边的条数,hehiad[i]表示以(i)为起点的边在数组中存储的第一个位置
  • head[1]=1,len[1]=3
  • head[2]=4,len[2]=1
  • head[3]=5,len[3]=1
  • head[4]=6,len[4]=2

利用前向星,可以在(O(nlog_2{n}))时间内排序+处理,(O(1))时间内查询

前向星特别适合优化SPFA,DFS和BFS

链式前向星

使用结构体,添加了当前边指向的下一条边的地址,head数组存放以i为起点的边的地址,next存放下一条边的地址,当next=0时表示最后一条边。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100501
struct NODE{
	int w;
	int e;
	int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[MAXN];
int cnt;
int head[MAXN]; 
void add(int u,int v,int w){
	edge[cnt].w=w;
	edge[cnt].e=v;    //edge[i]表示第i条边的终点 
	edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置 
	head[u]=cnt++;
}
int main(){
	memset(head,0,sizeof(head));
	cnt=1;
	int n;
	cin>>n;
	int a,b,c;
	while(n--){
		cin>>a>>b>>c;
		add(a,b,c);
	}
	int start;
	cin>>start;
	for(int i=head[start];i!=0;i=edge[i].next)
	   cout<<start<<"->"<<edge[i].e<<" "<<edge[i].w<<endl;
	return 0;
}

洛谷P1197 星球大战

基本思路

使用反并查集,使用链式前向星存储边的状况,vis表示是否被摧毁,由最后时刻反向重建,使用并查集检查联通块数目

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);
#define ll long long
#define INF  0x7f7f7f7f;
#define MAXN 500010
#define ok1 cout<<"OK1"<<endl
#define ok2 cout<<"OK2"<<endl
#define ok3 cout<<"OK3"<<endl
#define MOD 10007
using namespace std;
int f[MAXN],store[MAXN],ans[MAXN];
int cnt=0;bool vis[MAXN];	int h[MAXN];
typedef struct{
	int w,from,to,next;
}EDGE; EDGE edges[MAXN];
void add(int u,int v)
{//链式前向星 
	edges[cnt].from=u;
	edges[cnt].to=v;
	edges[cnt].next=h[u];
	h[u]=cnt++;
}
int find(int x)
{//路径压缩 
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
int main()
{
	int n,m,a,b,k,x;
	cin>>n>>m;

	fill(h,h+MAXN,-1);
	memset(vis,false,sizeof(vis));
	FOR(i,0,n)
	{
		f[i]=i;
	} 
	FOR(i,0,m)
	{
		cin>>a>>b;
		add(a,b);add(b,a);//无向图 
	}
	cin>>k;
	int total=n-k;
	FOR(i,0,k)
	{
		cin>>x;
		store[i]=x;
		vis[x]=true;//全部毁灭 
	}
	FOR(i,0,2*m)
	{
		if(vis[edges[i].from]==false&&vis[edges[i].to]==false)
		{//不是被毁灭的点 
			int fa=find(edges[i].from);
			int fb=find(edges[i].to);
			if(fa!=fb)
			{
				f[fa]=fb;
				total--;//计算联通块,若能本来不联通,联通块数目-- ,本来联通则不变 
			}
		}
	}
	ans[k]=total; //倒着重建
	for(int i=k-1;i>=0;i--) 
	{
		int u=store[i];
		total++;
		vis[u]=false;
		for(int j=h[u];j!=-1;j=edges[j].next)
		{
			if(vis[edges[j].to]==false){
				int fa=find(edges[j].from);
				int fb=find(edges[j].to);
				if(fa!=fb){
					f[fa]=fb;
					total--;//本来不连通,重建后联通 ,本来联通就不变 
				} 
			} 
		}
		ans[i]=total;
	} 
	FOR2(i,0,k) 
		cout<<ans[i]<<endl; 
	return 0;
}
原文地址:https://www.cnblogs.com/tldr/p/11284891.html