受歡迎的牛+Trajan縮點+樹形dp

題目鏈接:

題目解析:
題目數據: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

 */

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章