嘘~ 正在从服务器偷取页面 . . .

洛谷P6134 [JSOI2015]最小表示 题解


洛谷P6134 [JSOI2015]最小表示 题解

题目链接:P6134 [JSOI2015]最小表示

题意

对于一个 \(N\) 个点(每个点从 \(1\)\(N\) 编号),\(M\) 条边的有向图,cxy 发现,如果从图中删去一些边,那么原图的连通性会发生改变;而也有一些边,删去之后图的连通性并不会发生改变。

cxy 想知道,如果想要使得原图任意两点的连通性保持不变,我们最多能删掉多少条边呢?

为了简化一下大家的工作量,这次 cxy 保证他给定的有向图一定是一个有向无环图(cxy:大家都知道对于给任意有向图的问题,最后都能转化为有向无环图上的问题,所以就干脆简化一下大家的工作)。

输入格式

第一行包含两个正整数 \(N\)\(M\)

接下来 \(M\) 行,每行包含两个 \(1\)\(N\) 之间的正整数 \(x_i\)\(y_i\),表示图中存在一条从 \(x_i\)\(y_i\) 的有向边。

输入数据保证,任意两点间只会有至多一条边存在。

输出格式

输出一行包含一个整数,表示cxy最多可以删掉的边数。

数据范围

对于 \(100\%\) 的数据,\(1 \leq N\leq 3\times 10^4\)\(0 \leq M\leq 10^5\)

不难发现每对节点保留其最长的路径一定可以得到最优解。

证明:

感性理解的话,是因为保留这些边的过程最大化了边的利用价值。

考虑原图的一个拓扑序,显然它构成一棵树,且边均由子结点指向父结点

当然拓扑序不能反映原图的连通性,实际上这棵树上还有一些“返祖边”

这里“返祖边”类似于dfs树的返祖边,由某个结点指向它的某个祖先或祖先的兄弟。

如果 \(u\) 为叶子结点,显然仅留“返祖边”是最优的。

如果 \(u\) 不为叶子结点,我们首先会尝试让 \(u\) 的儿子连上 \(u\) ,然后尝试 \(u\) 的孙子...

不难发现这样一定是优的,因为连深度较大的结点只会让答案增大。

至此,我们证明了算法的正确性。\(\square\)

考虑用 \(\mathtt{bitset}\) 维护结点的连通性。

\(u\)\(v\) 连了一条有向边。

  • 我们用 link[u][v] 表示 \(u\)\(v\) 连通
  • 因为 \(v\) 能到达的结点 \(u\) 也能到达,因此我们可以直接 link[u] |= link[v]

时间复杂度 \(\mathcal{O}\left(\frac{n^2\log n}{32}\right)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(3e4+15))
#define M ((int)(1e5+15))

bitset<N> link[N];
int n,m,pos=1,head[N],to[N],in[N],id[N],dfn[N];
struct Edge{int u,v,next;} e[M];
void addEdge(int u,int v) { e[++pos] = {u,v,head[u]}; head[u] = pos; }
int topo()
{
    int res=0,tim=0; queue<int> q;
    for(int i=1; i<=n; i++) if(!in[i]) q.push(i);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        id[++tim] = u; dfn[u] = tim;
        for(int i=head[u]; i; i=e[i].next)
            if(!(--in[e[i].v])) q.push(e[i].v);
    }
    for(int k=n; k>=1; k--)
    {
        int u = id[k], cnt=0; link[u][u] = 1;
        for(int i=head[u]; i; i=e[i].next)
            to[++cnt] = e[i].v;
        sort(to+1, to+1+cnt, [](int a,int b) { return dfn[a] < dfn[b]; });
        for(int i=1; i<=cnt; i++)
            if(link[u][to[i]]) ++res;
            else link[u] |= link[to[i]];
    }
    return res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m;
    for(int i=1,u,v; i<=m; i++)
    {
        cin >> u >> v;
        addEdge(u,v); ++in[v];
    }
    cout << topo() << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/jasony/p6134-jsoi2015-zui-xiao-biao-shi


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录