拓扑排序,bitset~[JSOI2015]最小表示
题意:在有向无环图中删尽可能多的边,使图连通性不变,输出最大数量。
题解:写这题主要就是学一下bitset的用法,首先如果一个x到y的边可以删的话,说明从x到y有别的路可以走,从此还可以想到解此题的一个关键,如y可以到z,然后x连着y与z,我先让x和y连起来,则一次完成了x与y连通与z连通的任务,那么这个时候x到z就是条费边了,可加入答案,所以我们要将点的边进行排序,规则就是拓扑排序靠后的点排在后面,之后就是怎么记录y连着z,还有判断x到z是费边时怎么记录x已经与z连接起来了,这里我们用bitset去记录,这里就说几个关键的操作,bitset[x] [y]=1说明x已连接上y,bitset[x]|=bitset[y] 的意思是将y连接到的其他点也加入x。
总结一下,先用拓扑排序列出搜点的顺序,再从无出点的点(逆序)开始,往前加边,同时对边排序。
#include<iostream>
#include<algorithm>
#include<map>
#include<bitset>
#include<vector>
#include<queue>
using namespace std;
#define pb push_back
int n,m,v,u,ru[30007],dis[30007];
vector<int>ho[30007];
vector<int>edg;
queue<int>sa;
bitset<30007>se[30007];
bool cmp(int a,int b){
return dis[a]<dis[b];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
ho[u].pb(v);
ru[v]++;
}
for(int i=1;i<=n;i++){
if(ru[i]==0)sa.push(i);
}
int cnt=0;
while(!sa.empty()){
int lin=sa.front();
sa.pop();
dis[lin]=++cnt;
edg.pb(lin);
for(int i=0;i<ho[lin].size();i++){
ru[ho[lin][i]]--;
if(ru[ho[lin][i]]==0){
sa.push(ho[lin][i]);
}
}
}
for(int i=1;i<=n;i++){
sort(ho[i].begin(),ho[i].end(),cmp);
}
int len=edg.size()-1,lin,lin2,ans=0;
for(int i=len;i>=0;i--){
lin=edg[i];
se[lin][lin]=1;
for(int j=0;j<ho[lin].size();j++){
lin2=ho[lin][j];
if(se[lin][lin2]==1){
ans++;
}
else{
se[lin]|=se[lin2];
}
}
}
printf("%d
",ans);
return 0;
}