1. 程式人生 > 其它 >loj3501.「聯合省選 2021 A | B」圖函式

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;
}