20180429模擬賽T1——添邊問題
【問題描述】
沒有環的有向圖稱為有向無環圖,這是一個多麽美好的結構吖。
如果有一張有 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——添邊問題