zoukankan      html  css  js  c++  java
  • 树上计数-hdu-4705-Y

    题目链接:

    http://acm.hdu.edu.cn/showproblem.php?pid=4705

    题目大意:

    给一棵树,求三点不能通过简单路径到达的种数(也就是不在一条线上)。

    解题思路:

    如果枚举Y形的话,要先枚举一个点,然后在子树中找三个点,这样找有点麻烦,复杂度应该为o(n^2)。如果求补集,找三点在一条线的情况就好找了,枚举中间的那个点还剩下两个点,直接o(n)就可以解决(每次计算当前子树到和前面子树)。

    PS:树上计数:递归+dp+数学。一般都能很好解决。

    遍历树只用记录父亲节点判有没访问即可。

    代码:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<stack>
    #include<list>
    #include<queue>
    #define eps 1e-6
    #define INF 0x1f1f1f1f
    #define PI acos(-1.0)
    #define ll __int64
    
    #define lson l,m,(rt<<1)
    #define rson m+1,r,(rt<<1)|1
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    using namespace std;
    
    
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    //转化成补集,把四个点的问题转化成三个点,再在三个点中枚举中间点
    //树的计数问题以后要多做了,大部分与递归和dp和数学有关
    ll ans;
    
    #define Maxn 110000
    struct Edge   //无向边相当于有两条边
    {
        int v;
        struct Edge * next;
    }*head[Maxn<<1],edge[Maxn<<1];
    
    int n,num;
    
    void add(int a,int b)
    {
        ++num;
        edge[num].v=b;
        edge[num].next=head[a],head[a]=&edge[num];
    }
    
    ll cnt[Maxn];
    
    void dfs(int pre,int cur)
    {
        struct Edge * p=head[cur];
        ll sum=0; //sum记录该节点的所有子树总结点数
        cnt[cur]=1; //表示以该节点为树根的所有节点总数
        while(p)
        {
          if(p->v==pre)
          {
             p=p->next;
             continue;
          }
          dfs(cur,p->v);//先把子树计算好 递归很强大,顺序导致用法很灵活
          ans-=sum*cnt[p->v]; //当前子树和前面所有子树的节点,两棵树各选一个节点
          sum+=cnt[p->v];
          cnt[cur]+=cnt[p->v];//统计
          p=p->next;
        }
        ans-=sum*(n-cnt[cur]); //在子树中选一点 然后在非子树中选一点,和cur点
    }
    int main()
    {
       //freopen("01.in","r",stdin);
       //freopen("ans.out","w",stdout);
        int a,b;
        while(~scanf("%d",&n))
        {
            memset(head,NULL,sizeof(head));
            num=0;
            for(int i=1;i<n;i++)
            {
                scanf("%d%d",&a,&b);
                add(a,b);
                add(b,a);
            }
            ans=(ll)n*(n-1)*(n-2)/6;//求补
            dfs(-1,1);
            printf("%I64d
    ",ans);
        }
       return 0;
    }
    
    
    
    


  • 相关阅读:
    JS—超酷时钟
    JS—简单年历表
    ZZULI 1783 简单的求和
    ZZULI 1788 小金刚的宝藏(01背包模板)
    HDU 5172 GTY's gay friends(BestCoder Round #29)
    HDU 2157 How many ways??
    HDU 5171 GTY's birthday gift(BestCoder Round #29)
    在SQL Server 中启用 FileStream
    (转载)SQL Server的一个不显眼的功能 备份文件的分割
    面向对象建模方法与数据库建模方法的比较
  • 原文地址:https://www.cnblogs.com/bbsno1/p/3278104.html
Copyright © 2011-2022 走看看