P6134 [JSOI2015]最小表示
题意
给定一个有向无环图,节点编号为 ,有 条边,你可以删除其中的某些边,使其中任意两点的连通性保持不变(若原图中点 到 有一条路径,那么新图中 到 也有一条路径,反之亦然),求最多能删除几条边?
分析
先分析一下样例:

由样例解释得,边 和 是可以删去的,因为 可以用 代替; 可以用 代替,进而可以代替为 。
所以,一条边 可以被删去,仅当存在另一条路径可以替代它,题目中又保证任意两点之间至多只有一条边,所以另一条路径的长度必然大于等于 ,即存在一条路径 ,于是问题转化为求 。
设边集为 ,式子可进一步转化为 。
设 表示存在一条边 , 表示存在一条路径 ,即这条路径的边数大于等于 ,则答案为 。
我们对图进行拓补排序,遍历每条边 ,我们要用 和 更新 ,若 到 存在边数大于等于 的路径,那么经过 后,就存在边数大于等于 的路径了,即 。
这样做的时间复杂度是 , 最大可能为 ,不能通过此题,不过 部分可以使用 优化,复杂度为 ,可以通过此题。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=3e4+10;
int n,m,in[N],ans;
vector<int>e[N];
bitset<N>d[N],w[N];
void topu(){
queue<int>q;
for(int i=1;i<=n;i++)
if(!in[i])
q.push(i);
while(q.size()){
int x=q.front();q.pop();
for(int i=0,y;i<e[x].size();i++){
in[y=e[x][i]]--;
d[y]|=d[x]|w[x];
if(!in[y])
q.push(y);
}
}
}
int main(){
n=read();m=read();
for(int i=1,x,y;i<=m;i++){
x=read();y=read();
e[x].push_back(y);
in[y]++;
w[y][x]=1;
}
topu();
for(int i=1;i<=n;i++)
for(int j=0;j<e[i].size();j++)
ans+=d[e[i][j]][i];
write(ans);
return 0;
}

浙公网安备 33010602011771号