loj3501.「联合省选 2021 A | B」图函数
我咋觉得这题比 D1T2 不知道简单到哪里去了。
考虑这个函数,一个点对 \(i,j(i<j)\) 有贡献当且仅当 \(i\rightarrow j,j\rightarrow i\) 都只经过 \((i,n]\) 范围内的点。这是因为如果前面的点对函数有贡献,那么他会被删去,不能经过,否则那个点一定不在和目标点相连的强联通分量里面,也不会经过。这个显然可以 Floyd 做。
接下来考虑删边的问题,套路地倒过来变成加边,显然答案是单调不降的,我们只要求出每个点最早产生贡献的时间然后求前缀和即可。具体来讲我们只需要在跑 Floyd 的时候记录一下经过的边的编号的最大值,这个就是他最早产生贡献的时间。
时间复杂度 \(O(n^3+m)\),时限只有 1s,轻度卡常怎么 T2T3 都卡,需要在 Floyd 中特判+分类讨论减少一些循环次数。
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,g[1001][1001],ans[200005];
inline int read()
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}
void print(int x)
{
    if(x>=10)
        print(x/10);
    putchar(x%10+'0');
}
int main()
{
    n=read(),m=read();
    for(register int i=1;i<=m;++i)
    {
        int x=read(),y=read();
        g[x][y]=i;
    }
    ans[m+1]=n;
    for(register int k=n;k;--k)
        for(register int i=1;i<=n;++i)
            if(i!=k&&g[i][k])
            {
                if(i<k)
                {
                    for(register int j=1;j<=n;++j)
                        if(i!=j)
                            g[i][j]=max(g[i][j],min(g[i][k],g[k][j]));
                }
                else
                    for(register int j=1;j<k;++j)
                        g[i][j]=max(g[i][j],min(g[i][k],g[k][j]));
            }
    for(register int i=1;i<n;++i)
        for(register int j=i+1;j<=n;++j)
            if(min(g[i][j],g[j][i]))
                ++ans[min(g[i][j],g[j][i])];
    for(register int i=m;i;--i)
        ans[i]+=ans[i+1];
    for(register int i=1;i<=m+1;++i,putchar(' '))
        print(ans[i]);
    puts("");
    return 0;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号