POJ1144 網路
description:
給出一張\(N\)個點的無向圖,求其中割點的個數
data range:
\(N\le 100\)
solution:
一道模板題(但是讀入實在是把我噁心壞了)
#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
#include<sstream>
using namespace std;
const int N=105;
int n,dfn[N],low[N],sign;
vector<int>e[N];
bool flag[N];
string s;
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
int cd=0;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i],&lu=low[u];
if(v==fa)continue;
if(!dfn[v])
{
dfs(v,u);lu=min(lu,low[v]);
if(low[v]>=dfn[u]&&u!=fa)flag[u]=1;
if(u==fa)++cd;
}
else lu=min(lu,dfn[v]);
}
if(u==fa&&cd>1)flag[u]=1;
}
int main()
{
while(1)
{
cin>>n;if(!n)break;
for(int i=1;i<=n;++i)e[i].clear();
while(1)
{
int u;cin>>u;if(!u)break;
getline(cin,s);stringstream ss(s);
int v;
while(ss>>v)
e[u].push_back(v),e[v].push_back(u);
}
fill(flag+1,flag+n+1,0);
fill(dfn+1,dfn+n+1,0);
fill(low+1,low+n+1,0);sign=0;
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
printf("%d\n",count(flag+1,flag+n+1,1));
}
return 0;
}
POJ2117 Electricity
description:
給定一張\(N\)個點的無向圖,詢問刪除一個點後最多有多少個聯通分量
data range:
\(N\le 10^4\)
solution:
還是比較模板
求割點時順便統計下就行了
但要稍微注意下圖不連通的情況
#include<cstdio>
#include<vector>
using namespace std;
const int N=1e4+5;
int n,m,low[N],dfn[N],sign,ans;
vector<int>e[N];
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
int cd=u==fa?0:1;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i],&lu=low[u];
if(v==fa)continue;
if(!dfn[v])
{
dfs(v,u),lu=min(lu,low[v]);
if(u!=fa&&low[v]>=dfn[u])++cd;
else if(u==fa)++cd;
}
else lu=min(lu,dfn[v]);
}
ans=max(ans,cd);
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
if(!n&&!m)break;
for(int i=1;i<=n;++i)e[i].clear();
for(int i=1;i<=m;++i)
{
int u,v;scanf("%d%d",&u,&v);++u,++v;
e[u].push_back(v),e[v].push_back(u);
}
int cnt=0;sign=0,ans=0;
fill(dfn+1,dfn+n+1,0);
for(int i=1;i<=n;++i)
if(!dfn[i])++cnt,dfs(i,i);
printf("%d\n",ans+cnt-1);
}
return 0;
}
HDU3749 Financial Crisis
description:
給出一個\(N\)個點的無向圖,有\(Q\)次詢問,每次詢問給出兩個點\(u,v\),如果它們間沒有路徑,輸出\(zero\);否則如果它們間有至少兩條點不重複的路徑,輸出\(two\ or\ more\);否則輸出\(one\)
data range:
\(N\le 5*10^3\)
\(Q\le 10^3\)
solution:
首先如果兩個點不連通,那麼輸出\(zero\),這個可以用並查集來維護
注意到點不重複這四個字
那麼如果有至少兩條點不重複的路徑,那麼這兩個點一定是在同一個點雙聯通分量內的
其他情況都輸出\(one\)
p.s.這道題可以當做點雙的一個完整模板
code:
#include<cstdio>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int N=5005;
int n,m,q,fa[N],dfn[N],low[N],sign,cnt,id[N],kase;
struct edge{int u,v;edge(int _u=0,int _v=0){u=_u,v=_v;}};
stack<edge>s;
vector<int>e[N],bcc[N],bccs[N];
int fd(int x){return fa[x]==x?x:fa[x]=fd(fa[x]);}
inline void merge(int u,int v){u=fd(u),v=fd(v);if(u!=v)fa[u]=v;}
inline void add(int u,int t)
{
if(id[u]==t)return;
id[u]=t;bcc[t].push_back(u),bccs[u].push_back(t);
}
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i],&lu=low[u];
edge eg=edge(u,v);
if(v==fa)continue;
if(!dfn[v])
{
s.push(eg);
dfs(v,u),lu=min(lu,low[v]);
if(low[v]>=dfn[u])
{
++cnt;
while(1)
{
edge x=s.top();s.pop();
add(x.u,cnt),add(x.v,cnt);
if(x.u==u&&x.v==v)break;
}
}
}
else if(dfn[v]<dfn[u])
s.push(eg),lu=min(lu,dfn[v]);
}
}
inline int solve(int u,int v)
{
if(fd(u)!=fd(v))return 0;
vector<int>&bu=bccs[u],&bv=bccs[v];
for(int i=0;i<bu.size();++i)
for(int j=0;j<bv.size();++j)
if(bu[i]==bv[j]&&bcc[bu[i]].size()>2)return 2;
return 1;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&q)==3)
{
if(!n&&!m&&!q)break;
for(int i=1;i<=n;++i)
e[i].clear(),bcc[i].clear(),bccs[i].clear(),fa[i]=i;
for(int i=1;i<=m;++i)
{
int u,v;scanf("%d%d",&u,&v);++u,++v;
e[u].push_back(v),e[v].push_back(u);
merge(u,v);
}
fill(dfn+1,dfn+n+1,0);sign=0;cnt=0;
fill(id+1,id+n+1,0);
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
printf("Case %d:\n",++kase);
while(q--)
{
int u,v;scanf("%d%d",&u,&v);u++,v++;
int ans=solve(u,v);
puts(ans?(ans==1?"one":"two or more"):"zero");
}
}
return 0;
}
HDU4587 TWO NODES
description:
給出一個\(N\)個點的無向圖,詢問刪去其中兩個點後最多可以將其分成多少個聯通塊
data range:
\(N\le 5*10^3\)
solution:
考慮到資料範圍\(N\le 5000\)
因此我們可以先列舉其中一個被刪除的點
然後在剩餘的圖上計算刪去當前點後形成的聯通塊個數即可
還是注意下圖不連通的情況
code:
#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=5005;
int n,m,cut,dfn[N],low[N],sign,ans,anss;
vector<int>e[N];
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
int cd=u==fa?0:1;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i],&lu=low[u];
if(v==fa||v==cut)continue;
if(!dfn[v])
{
dfs(v,u),lu=min(lu,low[v]);
if(low[v]>=dfn[u]&&u!=fa)++cd;
else if(u==fa)++cd;
}
else if(dfn[v]<dfn[u])lu=min(lu,dfn[v]);
}
ans=max(ans,cd);
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
for(int i=1;i<=n;++i)e[i].clear();
for(int i=1;i<=m;++i)
{
int u,v;scanf("%d%d",&u,&v);++u,++v;
e[u].push_back(v),e[v].push_back(u);
}
anss=0;
for(int i=1;i<=n;++i)
{
cut=i;sign=0;int cnt=0;ans=0;
fill(dfn+1,dfn+n+1,0);
for(int j=1;j<=n;++j)
if(j!=cut&&!dfn[j])++cnt,dfs(j,j);
anss=max(anss,ans+cnt-1);
}
printf("%d\n",anss);
}
return 0;
}
POJ3177 分離的路徑(USACO 2006 Jan. Gold)
description:
給出一個\(N\)個點\(M\)條邊的無向圖,詢問至少新新增多少點後可以使得原圖邊雙聯通
data range:
\(N\le 5000\)
\(M\le 10^4\)
solution:
我們可以將原圖的所有極大邊雙連通分量縮點
然後參考有向圖新增儘量少的邊使得整個圖為強聯通的做法
我們只要將縮點後所有度數為1的點兩個一組地用一條邊相連即可
形式化地,設度數為1的點有\(x\)個,那麼就需要新增\(\lceil \frac{x}{2}\rceil\)條邊
具體做法其實沒有說的那麼麻煩
直接在原圖上找到左右的橋
然後再dfs一遍,強制不能經過橋,這樣就可以找到所有的邊雙了
最後列舉每條邊統計度數即可
#include<bits/stdc++.h>
using namespace std;
const int N=5005,M=2e4+5;
int n,m,tot=1,sign,cnt;
int fi[N],ne[M],to[M],dfn[N],low[N],id[N],deg[N];
bool flag[M];
inline void add(int x,int y){ne[++tot]=fi[x],fi[x]=tot,to[tot]=y;}
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
for(int i=fi[u];i;i=ne[i])
{
int v=to[i],&lu=low[u];
if(v==fa)continue;
if(!dfn[v])
{
dfs(v,u),lu=min(lu,low[v]);
if(low[v]>dfn[u])flag[i]=flag[i^1]=1;
}
else if(dfn[v]<dfn[u])lu=min(lu,dfn[v]);
}
}
void _dfs(int u,int col)
{
id[u]=col;
for(int i=fi[u];i;i=ne[i])
{
int v=to[i];
if(flag[i]||id[v])continue;
_dfs(v,col);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
for(int i=1;i<=n;++i)if(!id[i])_dfs(i,++cnt);
for(int i=1;i<=n;++i)
for(int j=fi[i];j;j=ne[j])
if(id[i]!=id[to[j]])++deg[id[i]],++deg[id[to[j]]];
int ans=count(deg+1,deg+cnt+1,2);
printf("%d\n",ans+1>>1);
return 0;
}
POJ3352 Road Construction
同上一道題
井下礦工 (Mining Your Own Business, WF2011, LA5135)
description:
在一個無向圖上選擇儘量少的點塗黑,使得刪除任意一個點後,每個連通分量裡都至少有一個黑點。
data range:
\(N\le 5*10^4\)
solution:
先求出所有的極大點雙連通分量
對於每個點雙連通分量單獨考慮,設其大小為\(sz\)
如果其內部割點個數\(>1\),那麼不用在其內部塗黑(因為如果某個割點被刪去後,還可以通過其他割點和別的聯通分量形成聯通塊)
如果其內部割點個數\(=1\),那麼需要在一個不是割點的位置塗黑,方案數就是\(sz-1\)(即去除割點)
如果其內部割點個數\(=0\),那麼就需要在其中任選兩個塗黑,方案數就是\(sz*(sz-1)/2\)(沒有割點就相當於這個塊是獨立的,泡不到其他塊,因此要選兩個點)
另外還是要注意圖不連通的情況
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,m,sign,cnt,low[N],dfn[N],id[N],fa[N],sz[N];
bool flag[N],pd[N];
vector<int>e[N],bcc[N];
int fd(int x){return fa[x]==x?x:fa[x]=fd(fa[x]);}
inline bool isnum(char &ch){return '0'<=ch&&ch<='9';}
inline int read()
{
int s=0,w=1; char ch=getchar();
for(;!isnum(ch);ch=getchar())if(ch=='-')w=-1;
for(;isnum(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s*w;
}
inline void add(int u,int v)
{
if(id[u]==v)return;
id[u]=v,bcc[v].push_back(u);
}
int stx[N],sty[N],top;
void dfs(int u,int pr)
{
low[u]=dfn[u]=++sign;
int cd=0;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i];
if(v==pr)continue;
if(!dfn[v])
{
++cd;stx[++top]=u,sty[top]=v;
dfs(v,u),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
++cnt;flag[u]=1;
while(1)
{
add(stx[top],cnt),add(sty[top],cnt);
if(stx[top]==u&&sty[top]==v)break;--top;
}
--top;
}
}
else if(dfn[v]<dfn[u])
{
stx[++top]=u,sty[top]=v;
low[u]=min(low[u],dfn[v]);
}
}
if(u==pr&&cd<=1)flag[u]=0;
}
int main()
{
for(int kase=1;;++kase)
{
m=read();if(!m)break;n=0;
for(int i=1;i<=m+1;++i)fa[i]=i,sz[i]=1;
for(int i=1;i<=m;++i)
{
int u=read(),v=read();n=max(n,max(u,v));
e[u].push_back(v),e[v].push_back(u);
u=fd(u),v=fd(v);
if(u!=v)fa[u]=v,sz[v]+=sz[u];
}
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
int ans1=0;ll ans2=1ll;
for(int i=1;i<=cnt;++i)
{
int num=0;
for(int j=0;j<bcc[i].size();++j)num+=flag[bcc[i][j]];
if(num==1)++ans1,ans2*=bcc[i].size()-1,pd[fd(bcc[i][0])]=1;
}
for(int i=1;i<=n;++i)
if(i==fd(i)&&!pd[i])
pd[i]=1,ans1+=min(sz[i],2),ans2*=sz[i]>1?1ll*sz[i]*(sz[i]-1)/2ll:1;
printf("Case %d: %d %lld\n",kase,ans1,ans2);
for(int i=1;i<=n;++i)e[i].clear(),bcc[i].clear();
top=cnt=sign=0;fill(dfn+1,dfn+n+1,0);
fill(id+1,id+n+1,0);fill(flag+1,flag+n+1,0);
fill(pd+1,pd+n+1,0);
}
return 0;
}
HDU 3394 Railway
description:
給一個\(N\)個點的無向圖,如果至少有兩個環共用了一些邊,那麼這些邊被認為是衝突邊,如果一些邊不在任何一個環中,這些邊被認為是多餘邊,問這個圖中有多少多餘邊和衝突邊
data range:
\(N\le 10^4\)
solution:
對於多餘邊,直接在原圖上找橋就可以了
以下考慮統計衝突邊
對於每一個點雙聯通分量,不妨設其中點的個數為\(cntp\),邊的個數為\(cnte\)
容易知道\(cntp\le cnte\)
如果\(cntp==cnte\)那麼會恰好形成一個簡單環
但如果\(cntp<cnte\),那麼這個聯通分量中所有邊都是衝突邊
上圖是\(cntp=cnte-1\)的情況,容易發現其中已經有3個環
然後就是模板了
code:
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=10005,M=1e5+5;
int n,m,tot=1,sign,ans1,ans2,cnt;
int fi[N],ne[M<<1],to[M<<1];
int dfn[N],low[N],vis[N];
bool flag[M<<1];
inline bool isnum(char &ch){return '0'<=ch&ch<='9';}
inline int read()
{
int s=0,w=1; char ch=getchar();
for(;!isnum(ch);ch=getchar())if(ch=='-')w=-1;
for(;isnum(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s*w;
}
inline void add(int x,int y){ne[++tot]=fi[x],fi[x]=tot,to[tot]=y;}
int stx[M],sty[M],top;
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
for(int i=fi[u];i;i=ne[i])
{
int v=to[i];
if(v==fa)continue;
if(!dfn[v])
{
stx[++top]=u,sty[top]=v;
dfs(v,u),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
int cnte=0,cntp=0;
++cnt;if(low[v]>dfn[u])++ans1;
for(;;)
{
int x=stx[top],y=sty[top];--top;++cnte;
if(vis[x]!=cnt)vis[x]=cnt,++cntp;
if(vis[y]!=cnt)vis[y]=cnt,++cntp;
if(x==u&&y==v)break;
}
if(cnte>cntp)ans2+=cnte;
}
}
else if(dfn[v]<dfn[u])
{
low[u]=min(low[u],dfn[v]);
stx[++top]=u,sty[top]=v;
}
}
}
int main()
{
while(1)
{
n=read(),m=read();if(!n&&!m)break;
fill(fi+1,fi+n+1,0);tot=1;
for(int i=1;i<=m;++i)
{
int u=read(),v=read();++u,++v;
add(u,v),add(v,u);
}
fill(flag+1,flag+tot+1,0);cnt=0;
fill(dfn+1,dfn+n+1,0);sign=0;ans1=ans2=0;
fill(vis+1,vis+n+1,0);
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
printf("%d %d\n",ans1,ans2);
}
return 0;
}
SPOJ - STC10. Blockade, POI 2008
description:
Byteotia 城市有\(n\)個城鎮,\(m\)條雙向道路。每條道路連線兩個不同的城鎮,沒有重複的道路,所有城鎮連通。輸出\(n\)個數,代表如果把與第\(i\)個點連線的所有邊去掉,將有多少對點不能互通。
data range:
\(N\le 10^5\)
solution:
對於每個點,刪去後至少有\(2*(n-1)\)個點對不連通(即其他點無法到達這個已經刪去的點)
對於割點,可以方便地處理出刪去後每個聯通塊的大小(即\(dfs\)樹上的子樹大小)
然後再加上聯通塊兩兩相乘之和的貢獻就是這個點的答案了
對於非割點,則沒有額外的貢獻
code:
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n,m,sign,dfn[N],low[N];
vector<int>e[N];
ll ans[N];
inline bool isnum(char &ch){return '0'<=ch&&ch<='9';}
inline int read()
{
int s=0,w=1; char ch=getchar();
for(;!isnum(ch);ch=getchar())if(ch=='-')w=-1;
for(;isnum(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s*w;
}
inline ll work(vector<int>&p)
{
ll anss=0,sm=0;
for(int i=0;i<p.size();++i)
anss+=sm*p[i],sm+=1ll*p[i];
return anss;
}
int dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
int sz=1,cd=0,now=0;vector<int>p;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i];
if(v==fa)continue;
if(!dfn[v])
{
++cd;int vsz=dfs(v,u);
low[u]=min(low[u],low[v]),sz+=vsz;
if(low[v]>=dfn[u])p.pb(vsz),now+=vsz;
}
else low[u]=min(low[u],dfn[v]);
}
if(u==fa&&cd<=1)p.clear();
if(u!=fa)p.pb(n-now-1);
ans[u]+=work(p);//p中數的兩兩相乘之和
return sz;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
e[u].pb(v),e[v].pb(u);
}
for(int i=1;i<=n;++i)ans[i]=1ll*(n-1);
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
for(int i=1;i<=n;++i)printf("%lld\n",ans[i]<<1);
return 0;
}
圓桌騎士 (Knights of the Round Table,LA3523)
description:
給出一個\(N\)個點的無向圖,求出其中有多少個點滿足它們不在任何一個長度為奇數的簡單環上
data range:
\(N\le 10^3\)
solution:
首先對於一個奇環,它一定位於一個點雙聯通分量之中
於是對原圖處理出所有點雙
單獨考慮一個點雙,如果其中不存在奇環(即是一個二分圖),那麼其中的點都是滿足條件的
否則,對於奇環上的點一定不滿足,那對於奇環以外的點呢?
首先由於連通性,因此存在一條路徑從v到u1
又由於雙連通性,因此存在一條路徑從v到u2(兩條點不重複的路徑分別是v->u2和v->u1->u2)
那麼又由於這個環是一個奇環,所以v也一定位於一個奇環上(綠色的環或者紅色的環)
然後就可做了
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,sign,cnt,dfn[N],low[N],id[N],col[N];
vector<int>bcc[N],e[N];
bool G[N][N],pd[N],flag[N];
int stx[N*N],sty[N*N],top;
inline void add(int u,int t)
{
if(id[u]==t)return;
id[u]=t,bcc[t].push_back(u);
}
void dfs(int u,int fa)
{
low[u]=dfn[u]=++sign;
int cd=0;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i];
if(v==fa)continue;
if(!dfn[v])
{
++cd;stx[++top]=u,sty[top]=v;
dfs(v,u),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
++cnt;
for(;;)
{
int x=stx[top],y=sty[top];--top;
add(x,cnt),add(y,cnt);
if(x==u&&y==v)break;
}
}
}
else if(dfn[v]<dfn[u])
{
stx[++top]=u,sty[top]=v;
low[u]=min(low[u],dfn[v]);
}
}
}
bool bwpd(int u,int c)
{
if(col[u]>=0)return col[u]==c;
col[u]=c;bool o=1;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i];
if(!flag[v])continue;
o&=bwpd(v,c^1);
}
return o;
}
int main()
{
while(scanf("%d%d",&n,&m)==2&&n&&m)
{
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(i!=j)G[i][j]=1;
while(m--)
{
int u,v;scanf("%d%d",&u,&v);
G[u][v]=G[v][u]=0;
}
for(int i=1;i<=n;++i)e[i].clear();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(G[i][j])e[i].push_back(j);
for(int i=1;i<=n;++i)bcc[i].clear();
fill(dfn+1,dfn+n+1,0);top=sign=cnt=0;
fill(id+1,id+n+1,0);fill(flag+1,flag+n+1,0);
for(int i=1;i<=n;++i)if(!dfn[i])dfs(i,i);
fill(col+1,col+n+1,-1);fill(pd+1,pd+n+1,0);
for(int i=1;i<=cnt;++i)
{
for(int j=0;j<bcc[i].size();++j)flag[bcc[i][j]]=1;
if(!bwpd(bcc[i][0],1))
for(int j=0;j<bcc[i].size();++j)pd[bcc[i][j]]=1;
for(int j=0;j<bcc[i].size();++j)flag[bcc[i][j]]=0,col[bcc[i][j]]=-1;
}
printf("%d\n",count(pd+1,pd+n+1,0));
}
return 0;
}