学霸的期末 解题报告

简要题意

给定 \(n\) 个点 \(m\) 条边的有向图,在不改变图的连通性的前提下,删除任意条边后,最少可以保留多少条边;和任意加边后,最多可以包含多少条边。

数据范围:\(nm \le 5 \times 10 ^7,n \le 10^3,m\le 10^5\)

分析

先做第二问,等价于有多少有序点对 \((u,v)\) 满足 \(u\) 可达 \(v\),直接搜索即可。

再看第一问,我们考虑先缩点,然后在一个非单点的强连通分量中,我们可以只保留强连通分量大小条边;然后对于缩点后的新图,可以在 \(O(n^2)\) 的时间内预处理每一个点可以到达的点集,和可以到达一个点的点集,我们枚举每一条边 \((u,v)\),求 \(u\) 可到点集和可到 \(v\) 点集的交集,如果交集为空,那么该边必须保留。用 bitset 优化,时间复杂度为 \(O(\dfrac{nm}{w})\)

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define ls (i<<1)
#define rs (i<<1|1)
#define bmod(x) ((x)>=p?(x)-p:(x))
#define lowbit(x) ((x)&(-(x)))
#define End {printf("NO\n");exit(0);}
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline void ckmx(int &x,int y){x=(x>y)?x:y;}
inline void ckmn(int &x,int y){x=(x<y)?x:y;}
inline void ckmx(ll &x,ll y){x=(x>y)?x:y;}
inline void ckmn(ll &x,ll y){x=(x<y)?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
char buf[1<<20],*p1,*p2;
#define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? EOF : *p1++) : *p1++)
#define read() ({\
    int x = 0, f = 1;\
    char c = gc();\
    while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = gc();\
    while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc();\
    f * x;\
})
void write(int x){
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int N=2050;
int n,m,ans,ans0;
struct Edge{int from,f,to;}e[N<<1];
int num,h[N];
void add(int f,int t){e[++num].from=h[f],e[num].f=f,e[num].to=t,h[f]=num;}
int dfn[N],low[N],s[N],tail,col[N],sz[N],dfu,cnt;
bool in[N];
void tarjan(int u){
    dfn[u]=low[u]=++dfu;
    s[++tail]=u,in[u]=true;
    for(int i=h[u];i;i=e[i].from){
        int v=e[i].to;
        if(!dfn[v])
            tarjan(v),ckmn(low[u],low[v]);
        else if(in[v])
            ckmn(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        ++cnt;
        while(tail){
            int v=s[tail];--tail;
            in[v]=false;
            col[v]=cnt;
            ++sz[cnt];
            if(v==u) break;
        }
    }
}
struct pair_hash{
    template <class T1,class T2>
    size_t operator() (const pair<T1,T2>& p) const{
        auto hash1=hash<T1>{}(p.first);
        auto hash2=hash<T2>{}(p.second);
        return hash1^(hash2<<15);
    }
};
unordered_set<pii,pair_hash> t;
void build_DAG(){
    num=0;
    For(i,1,n) h[i]=0;
    For(i,1,m){
        int u=e[i].f,v=e[i].to;
        if(col[u]==col[v] || t.find(pii(col[u],col[v]))!=t.end()) continue;
        t.insert(pii(col[u],col[v]));
        add(col[u],col[v]);
    }
}
void dfs1(int u){
    if(in[u]) return;
    ++ans;
    in[u]=true;
    for(int i=h[u];i;i=e[i].from)
        dfs1(e[i].to);
}
bitset<N> to[N],from[N];
void dfs(int u,int st){
    if(to[st][u]) return;
    to[st][u]=from[u][st]=1;
    for(int i=h[u];i;i=e[i].from)
        dfs(e[i].to,st);
}
int main()
{
#if !ONLINE_JUDGE
    freopen("compare.in","r",stdin);
    freopen("compare.out","w",stdout);
#endif 
    n=read(),m=read();
    int u,v;
    For(i,1,m) u=read(),v=read(),add(u,v);
    For(i,1,n){
        For(j,1,n) in[j]=false;
        dfs1(i),--ans;
    }
    For(i,1,n)
        if(!dfn[i])
            tarjan(i);
    // For(i,1,n) printf("%d ",col[i]);
    // putchar('\n');
    build_DAG();
    For(i,1,cnt) dfs(i,i);
    For(i,1,cnt) from[i][i]=to[i][i]=false;
    For(i,1,cnt) ans0+=(sz[i]>1 ? sz[i] : 0);
    For(i,1,num){
        int u=e[i].f,v=e[i].to;
        // printf("%d %d\n",u,v);
        if((to[u]&from[v]).count()==0) ++ans0;
    }
    printf("%d %d\n",ans0,ans);
    return 0;
}
posted @ 2025-10-16 12:07  XiaoZi_qwq  阅读(2)  评论(0)    收藏  举报