[JSOI2015]最小表示

时间:2019-10-22
本文章向大家介绍[JSOI2015]最小表示,主要包括[JSOI2015]最小表示使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

BZOJ

题意:给定一个\(N\)个点\(M\)条边的有向无环图,如果想要使得原图任意两点的连通性保持不变,最多能删掉多少条边?\(N<=30000,M<=100000.\)

分析:对于一条边\((u,v)\),它能被删掉,当且仅当有另外一条路径\(u->v\)的路径.

对于有向无环图,需要想到拓扑排序,算是一个看到有向无环图的思路之一.

拓扑排序时给每个节点\(i\)记录一个访问时间\(num[i]=tim\),然后把每个节点的出边按照到达的节点的访问时间从小到大排序.然后按照访问时间从大到小遍历整张图.开一个\(N*N\)\(bitset\)记录对于每个节点\(i\),它当前是否能够到达节点\(j\),如果能则\(f[i][j]=1\).遍历到一个节点\(u\)时,扫描和它相连的节点\(v\),如果\(f[u][num[v]]\)已经等于1了,则这条边是可以删的,否则令\(f[u][num[v]]=1\),同时\(f[u]|=f[v]\),表示保留\((u,v)\)这条边之后,v能够到达的点,u通过v都能够到达(根据拓扑序,v在u之前已经更新完了).

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=30005;
const int M=100005;
int n,m,tim,ans,deg[N],num[N],st[N];
vector<int>g[N];queue<int>q;bitset<N>f[N];
inline void topsort(){
    for(int i=1;i<=n;++i)if(!deg[i])q.push(i);
    while(q.size()){
        int u=q.front();q.pop();
        num[u]=++tim;st[tim]=u;
        for(int i=0;i<(int)g[u].size();++i){
            int v=g[u][i];--deg[v];
            if(!deg[v])q.push(v);
        }
    }
}
inline bool cmp(const int &x,const int &y){return num[x]<num[y];}
int main(){
    n=read();m=read();
    for(int i=1;i<=m;++i){
        int u=read(),v=read();
        g[u].push_back(v);++deg[v];
    }
    topsort();
    for(int i=1;i<=n;++i)
        if(g[i].size())sort(g[i].begin(),g[i].end(),cmp);
    for(int i=tim;i>=1;--i){
        int u=st[i];
        for(int j=0;j<(int)g[u].size();++j){
            int v=g[u][j];
            if(f[u][num[v]]==1)++ans;
            else f[u][num[v]]=1,f[u]|=f[v];
        }
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11719428.html