【BZOJ4484】最小表示(JSOI2015)-贪心+拓扑排序+bitset
测试地址:最小表示
做法:本题需要用到贪心+拓扑排序+bitset。
显然,如果一条边对连通性没有影响,那肯定是要删掉的。现在的问题就是如何找到这些边。
我们考虑在反拓扑序上求。考虑一个点的所有出边,对于每个指向的点,如果当前还没有找到从当前点到这个点的路径,那么当前的边就要保留,并用这个点能到达的点的集合更新当前点的集合,这个显然能用bitset做到的复杂度。
但这样做有一个问题,如果到有边,到有边,到有边,那么到这条边是需要被删的,但如果在考虑点时使用了的顺序,我们就无法求出这样的边。实际上,这是因为从能到达,所以我们应该先连接,就能求出这样的边了。
上面讨论的特殊情况的通用形式就是,我们贪心地先考虑最“顶端”的那些点,就能考虑到所有要删的边了。这其实就是把所有指向的点按拓扑序排序。那么总复杂度就是,可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,tot=0,first[30010]={0},in[30010]={0},q[30010],pos[30010];
int son[30010];
struct edge
{
int v,next;
}e[100010];
bitset<30010> S[30010];
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
in[b]++;
}
void toposort()
{
int t=0;
for(int i=1;i<=n;i++)
if (!in[i])
{
q[++t]=i;
pos[i]=t;
}
for(int i=1;i<=n;i++)
{
int v=q[i];
for(int j=first[v];j;j=e[j].next)
{
in[e[j].v]--;
if (!in[e[j].v])
{
q[++t]=e[j].v;
pos[e[j].v]=t;
}
}
}
}
bool cmp(int a,int b)
{
return pos[a]<pos[b];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b);
}
toposort();
int ans=0;
for(int i=n;i>=1;i--)
{
S[q[i]].set(q[i]);
int siz=0;
for(int j=first[q[i]];j;j=e[j].next)
son[++siz]=e[j].v;
if (!siz) continue;
sort(son+1,son+siz+1,cmp);
for(int j=1;j<=siz;j++)
{
if (S[q[i]][son[j]]) ans++;
else S[q[i]]|=S[son[j]];
}
}
printf("%d",ans);
return 0;
}