題目解析:
題目數據:100%的數據N<=10000,M<=50000;
顯然這個圖裏面會有環,而我們可以做的是:判斷這個點是否是其它點的子節點;
因此:要把這個圖轉化爲樹;
用到Trajan算法;
在這道題中:可能會出現如下情況:
a-->b<--c;
|
|
V(箭頭)
d
就是有a ,c這兩個入度爲0的點;
所以要用一個for循環,控制Trajan算法的進行,保證所有的點都已經更新;
for(int i=1;i<=n;i++)
{
if(!dfn[i]) {
tarjan(i);
}
}
如果是第一次接觸Trajan算法,推薦你閱讀我的另一篇博客,裏面有一些對於該算法所使用變量的註釋
博客鏈接
下面接着分析:
將圖更新爲樹後,如何尋找所有點的子節點;
第一種:樹形dp的方法
第二種:直接判斷樹中,出度爲0的點有幾個,如果有一個,輸出對應的點包含幾個原始點;
有過有多個,直接輸出0;
第一種:
樹形dp如何寫,
a-->b<--c;
|
|
V(箭頭)
d
b被父節點更新時,判斷b是否已經被更新過,如果沒被更新(也就是要第一次更新)
那麼就把自身的值加上,傳遞給d的值,也就是b目前的值;
如果b已經被更新,那麼b所有的子節點也一定被更新了,這是b增加的只是父節點給它更新的值
b傳給子節點的值,也是它的父節點傳給它的值;
詳細看代碼。
#include<map>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int num=10010;
/*---------------------*/
struct node{
int u,v,next;
bool flag;
}e[num*5];
int head[num],ip0[num],cnt;
map<pair<int,int>,bool>mp;
int head2[num],ip[num],op[num],cnt2;
/*---------------------*/
int dfn[num],low[num],ind,stack[num],t,f[num],cnt3,w[num],all;
bool vis[num];
/*---------------------*/
int n,m,ans,dp[num];
/*---------------------*/
void int_i(void)
{
cnt=-1;
mp.clear();
memset(ip0,0,sizeof(ip0));
memset(head,-1,sizeof(head));
cnt2=-1;
memset(ip,0,sizeof(ip));
memset(op,0,sizeof(op));
memset(head2,-1,sizeof(head2));
ind=0;
memset(dfn,0,sizeof(dfn));
memset(low,-1,sizeof(low));
memset(vis,0,sizeof(vis));
t=0;
memset(stack,0,sizeof(stack));
cnt3=0;
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
all=n;
ans=0;
memset(dp,0,sizeof(dp));
return ;
}
void addedge(int u,int v)
{
e[++cnt].u=u;
mp[make_pair(u,v)]=true;
e[cnt].v=v;
e[cnt].flag=false;
e[cnt].next=head[u];
head[u]=cnt;
ip0[v]++;
return ;
}
void addedge2(int u,int v)
{
e[++cnt2].u=u;
e[cnt2].v=v;
e[cnt2].flag=false;
e[cnt2].next=head2[u];
head2[u]=cnt2;
op[u]++;ip[v]++;
return ;
}
void tarjan(int u)
{
int v;
dfn[u]=low[u]=++ind;
stack[++t]=u;
vis[u]=true;
for(int i=head[u];i!=-1;i=e[i].next)
{
if(e[i].flag) continue;
v=e[i].v;
//printf("v==%d\n",v);
e[i].flag=true;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
{
low[u]=min(low[u],dfn[v]);
}
}
//printf("u==%d,dfn==%d,low==%d\n",u,dfn[u],low[u]);
if(dfn[u]==low[u])
{
cnt3++;
do{
v=stack[t--];
vis[v]=false;
f[v]=cnt3;
w[cnt3]++;
//printf("cnt3==%d,v==%d,w==%d\n",cnt3,v,w[cnt3]);
}while(v!=u);
}
return ;
}
void dfs(int u,int f,int c)
{
int v;
for(int i=head2[u];i!=-1;i=e[i].next)//注意這裏是head2,不是head,不要寫混了;
{
v=e[i].v;
if(v==f) continue;
if(dp[v]==0) {//當前結點第一次更新,加上自身的
dp[v]=w[v]+c;
dfs(v,u,dp[v]);//如果當前結點爲0,那麼它所在的這條路徑沒有更新過它的子節點
}
else if(dp[v]!=0)
{
dp[v]+=c;
dfs(v,u,c);
}
if(dp[v]==all) ans+=w[v];
//這裏要寫w[v],不能寫+1;因爲可能滿足條件的一個點,
//包含了許多點
}
return ;
}
//第二種方法:
void solve(void)
{
int c=0,u=0;
for(int i=1;i<=cnt3;i++)
{
if(op[i]==0)
{
c++;
u=i;
}
}
if(c==1){
ans=w[u];
}
else
ans=0;
return ;
}
int main()
{
int u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
int_i();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
if(!mp[make_pair(u,v)])
addedge(u,v);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) {
tarjan(i);
}
}
for(int i=0;i<=cnt;i++)
{
u=e[i].u;
v=e[i].v;
if(f[u]!=f[v])
{
// printf("f[u]==%d,f[v]==%d\n",f[u],f[v]);
addedge2(f[u],f[v]);//(f[u],f[v]) ;not (u,v)
}
}
if(cnt3==1)//說明原圖只有一個點或只有一個環
{
printf("%d\n",n);
continue;
}
for(int i=1;i<=cnt3;i++)
{
if(ip[i]==0)
{
//printf("fa===%d\n",i);
dfs(i,-1,w[i]);
}
}
//for(int i=1;i<=cnt3;i++)
// printf("dp==%d\n",dp[i]);
//第二種方法:
//solve();
printf("%d\n",ans);
}
return 0;
}
/*
3 3
1 2
2 1
2 3
4 3
1 3
2 3
3 4
*/