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

前向星

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

  • \(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(n\log_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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值