P6134 [JSOI2015]最小表示 题解
首先有向无环图,考虑拓扑排序,接下来按照 \(BFS\) 来理解整个拓扑排序。
相当于把所有的点分成了若干层,(每一层中的点互不联通)。
那么保留最少的边一定是将每一层形成一个类似链的关系,也就是应该优先保留 \(u\) 能够到达的更浅的层的边。
于是对于当前的点 \(u\) ,每一条边按照出点 \(v\) 的时间戳排序。
因为是最优策略,所以按照上面的次序访问边 \(u->v\) 如果说 \(u,v\) 已经联通,这个边可以删去,连通性和最优性都可以保证,用一个 bitset
维护连通性就好了。
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e4+2,M=1e5+3;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)) f|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=f?-x:x;
}
template <typename T>
inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10^48);
}
bitset<N> bis[N];
int n,m;
int head[N],Next[M],to[M],dfn[N],tot,in[N],tim,rev[N];
int ju[N];
inline void Addedge(int u,int v){to[++tot]=v,Next[tot]=head[u],head[u]=tot;return;}
inline bool Cmp(int x,int y){return dfn[x]<dfn[y];}
inline void Topo(){
queue<int> q;
for(register int i=1;i<=n;++i) if(!in[i]) q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
dfn[x]=++tim;rev[tim]=x;
for(register int i=head[x];i;i=Next[i]){
int y=to[i];
if(!(--in[y])) q.push(y);
}
}
int ans=0;
for(register int i=n;i;--i){
int x=rev[i];
bis[x][x]=1;
int siz=0;
for(register int j=head[x];j;j=Next[j])
ju[++siz]=to[j];
sort(ju+1,ju+siz+1,Cmp);
for(register int j=1;j<=siz;++j){
if(bis[x][ju[j]]) ans++;
else bis[x]|=bis[ju[j]];
}
}
print(ans);
return ;
}
int main(){
read(n),read(m);
for(register int i=1;i<=m;++i){
int u,v;read(u),read(v);
Addedge(u,v);in[v]++;
}
Topo();
return 0;
}