P7516 [省选联考 2021 A/B 卷] 图函数

先考虑不删边的情况。
考虑对于 \(f(u,G)\),能做贡献的点符合那些条件。
假设有两个点 \(i,j\) 满足 \(i<j\),如果 \(j\) 能做贡献,且 \(i\) 能到 \(j\)\(j\) 能到 \(i\),那么 \(i\) 也一定能做贡献,所以已经被删了。枚举到 \(u\) 时,会发现 \(u\) 满足条件,然后就把 \(u\) 删了,后面的点都无法满足条件。
所以一个点能做贡献当且仅当只通过编号大于等于祂的点,能与 \(u\) 互相到达。对于一个点对 \(u,v(u<v)\) 如果只通过编号大于等于 \(v\) 的点能互相到达,那么在计算 \(f(v,G)\) 时,\(v\) 会做出贡献。
不删边的时候可以用 \(Floyd\)\(\mathcal{O}(n^3)\) 解决。
考虑加上删边操作。
发现删边是从小到大删的好像不用发现,所以每次可用的边是所有边的一个后缀。考虑设 \(f_{i,j}\) 表示只通过编号大于或等于 \(\min(u,v)\) 的点,使得 \(i,j\) 互相连通必须使用的最小的边最大是多少,然后就可以用 \(Floyd\mathcal{O}(n^3)\) 做了。

代码

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=1010;
int n,m,f[maxn][maxn],ans[maxn*maxn];
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a>b?a:b;}
signed main(){
    read(n,m);
    for(int i=1;i<=m;i++){
        int u,v;read(u,v);
        f[u][v]=i;
    }
    for(int k=n;k>=1;k--){
        for(int i=k+1;i<=n;i++) ans[min(f[i][k],f[k][i])]++;
        for(int i=1;i<=n;i++){
            if(!f[i][k]||i==k) continue;
            if(i>k) for(int j=1;j<k;j++) f[i][j]=max(f[i][j],min(f[i][k],f[k][j]));
            else for(int j=1;j<=n;j++) f[i][j]=max(f[i][j],min(f[i][k],f[k][j]));
        }
    }
    ans[m+1]=n;
    for(int i=m;i>=1;i--) ans[i]+=ans[i+1];
    for(int i=1;i<=m+1;i++) write(ans[i]),write(" ");
    return 0;
}
posted @ 2026-03-06 21:35  Link-Cut_Trees  阅读(5)  评论(0)    收藏  举报