[BZOJ4484][JSOI2015]最小表示(拓扑排序+bitset)

有一个结论:对于边<u,v>,若这是u到v的唯一路径,则这条边显然不可被删去,否则必然可以被删去。

因为若u到v还有其它路径,则必然是从u到某个点x再到v,由于最终答案中连通性不变,也就是最终答案中仍然可以走到x后再走到v,于是可以删去这条边。

于是大致算法就出来了:按拓扑序从后往前做,每次将这个点的所有出边到达的点按拓扑序从小到大处理,若某个出点已经可以被到达了则删边。

bitset维护连通性,$O(nm/32)$

 1 #include<cstdio>
 2 #include<bitset>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 6 using namespace std;
 7 
 8 const int N=100010;
 9 int n,m,cnt,x,y,p[N],id[N],d[N],q[N],v[N],t,ans,h[N],to[N],nxt[N];
10 bitset<30010> a[30010],b;
11 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
12 
13 void Top(){
14     int st=0,ed=0;
15     rep(i,1,n) if (!d[i]) q[++ed]=i,id[i]=ed;
16     while (st<ed){
17         int x=q[++st];
18         For(i,x) if (!--d[k=to[i]]) q[++ed]=to[i],id[to[i]]=ed;
19     }
20 }
21 
22 bool cmp(const int &a,const int &b){ return id[a]<id[b]; }
23 
24 int main(){
25     scanf("%d%d",&n,&m);
26     rep(i,1,m) scanf("%d%d",&x,&y),add(x,y),d[y]++;
27     Top();
28     for (int i=n; i; i--){
29         int x=q[i],tot=0; a[x][x]=1;
30         For(j,x) v[++tot]=k=to[j];
31         sort(v+1,v+tot+1,cmp);
32         rep(j,1,tot) if (a[x][v[j]]) ans++; else a[x]|=a[v[j]];
33     }
34     printf("%d\n",ans);
35     return 0;
36 }

 

posted @ 2018-12-02 20:20  HocRiser  阅读(166)  评论(0编辑  收藏  举报