1. 程式人生 > >20180429模擬賽T1——添邊問題

20180429模擬賽T1——添邊問題

不難 ast 無環 輸入 HA 我們 分享 描述 source

【問題描述】

沒有環的有向圖稱為有向無環圖,這是一個多麽美好的結構吖。

如果有一張有 N 個點的有向圖,我們可能需要刪掉一些邊使它變成一張有向無環圖。假設初始時我們只有 N 個互不相連的點,當然它也是一張有向無環圖。依次給出 T 條邊和每條邊的方向。 每給出一條邊就要立即決定是否要加入這一條邊,使得這張圖始終是一張有向無環圖(意思是:按順序處理每條邊,能加就加,讓你模擬這個過程,自環不能加入)。計算在滿足要求的情況下一共有多少條邊沒有被加入。如果所有邊都可以加入這張圖則輸出 0。

【輸入格式】

第一行為兩個整數:N(1<=N<=250),T(0<=T<=100,000)。接下來 T 行,每行兩個整數 x,y(1 <=x,y<= N),表示一條從 x 到 y 的單向邊。

【輸出格式】

一個整數,表示沒有被加入的邊數。

【樣例輸入】

3 6
1 2
1 3
3 1
2 1
1 2
2 3

【樣例輸出】

2

【樣例說明】

1-->2,之前 2-->1 沒有路徑,不會造成環,加入

1-->3,之前 3-->1 沒有路徑,不會造成環,加入

3-->1,之前 1-->3 有路徑,使得圖有環,不加入

2-->1,之前 1-->2 有路徑,不加入

1-->2 , 之前 2-->1 沒有路徑,加入

2-->3,之前 3-->2 沒有路徑,加入

因此答案是 2

【數據規模】

對於40%的數據,n<=50,T<=1000

對於90%的數據,n<=150,T<=100000

對於100%的數據,n<=250,T<=100000

題解

我們用\(f[i][j]\)表示\(i\)是否能到\(j\)(1:能 0:不能)。首先忽略重邊。判斷一條邊能否加入顯然是\(O(1)\)的,只要看看\(f[y][x]\)是不是\(1\)即可。

若加入,就要維護連通性。

如圖,對於兩個點\(A,B\),圈表示點集,箭頭表示邊,連邊\(A\to B\)後我們又要連藍邊與綠邊。

技術分享圖片

不難發現,我們可以把\(A\)與藍色集合、\(B\)與綠色集合合並,於是問題就變成了:

技術分享圖片

藍色集合向綠色集合連邊。

藍色集合為能到達\(A\)的點集;綠色集合表示能到達\(B\)

的點集。

\(n^2\)大力連邊即可。

我的代碼

#include <cstring>
#include <fstream>

using namespace std;

ifstream fin("stock.in");
ofstream fout("stock.out");

const int maxm=100000;
const int maxn=256;

int a[maxn][maxn];
int b[maxm],c[maxm];

int main()
{
    int n, m;
    fin >> n >> m;
    int ans=0;
    for(int i=0; i<m; ++i)
    {
        int aa, bb;
        fin >> aa >> bb;
        if(aa==bb || a[bb][aa]) ans++;
        else
        {
            int k1=0;
            int k2=0;
            b[++k1]=aa;
            c[++k2]=bb;
            for(int j=1; j<=n; ++j)
                if(a[j][aa] && !a[j][bb])
                    b[++k1]=j;
            for(int j=1; j<=n; ++j)
                if(a[bb][j] && !a[aa][j])
                    c[++k2]=j;
            for(int j=1; j<=k1; ++j)
                for(int k=1; k<=k2; ++k)
                    a[b[j]][c[k]]=1;
        }
    }
    fout << ans << \n;
    return 0;
}

bitset優化

以上代碼是可過的,但還可以用bitset優化一下,上一發zd大佬的代碼。

#include <iostream>
#include <bitset>
#include <cstdio>

using namespace std;

typedef int ll;

inline char gc()
{
    static char buf[1<<14],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++;
}

#define dd c=gc()
inline ll read()
{
    ll x=0,f=1;
    char dd;
    for(; !isdigit(c); dd)if(c==‘-‘)f=-1;
    for(; isdigit(c); dd)x=(x<<1)+(x<<3)+(c^48);
    return x*f;
}
#undef dd

inline void write(ll x)
{
    if(x<0)putchar(‘-‘),x=-x;
    if(x>9)write(x/10);
    putchar(x%10|48);
}

bitset<252>a[252];

int main()
{
    freopen("stock.in","r",stdin);
    freopen("stock.out","w",stdout);
    register ll n=read(),ans=0;
    for (register ll i=0; i<n; ++i) a[i][i]=1;
    for (register ll m=read(),x,y; m; --m)
    {
        x=read()-1,y=read()-1;
        if (a[y][x]) ++ans;
        else if (!a[x][y])
            for (register ll i=0; i<n; ++i)
            {
                if (a[i][x]) a[i]=a[i]|a[y];
            }
    }
    return write(ans),0;
}

20180429模擬賽T1——添邊問題