受欢迎的牛+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

 */

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