• HDU 4705 Y 树形枚举


    树形枚举--搜索

    题目描述:

    给你一棵树,要在一条简单路径上选3个不同的点构成一个集合,问能构成多少个不同的集合。

    解法:

    枚举所有结点,假设某个结点有n棵子树,每棵子树的结点个数分别为s1,s2,````sn.那么在选中该结点后,剩下的两个结点从子树上选,考虑顺序,则有方案数ans =  s1*(sum(si) - s1) + s2*(sum(si) - s2) + ``` + sn*(sum(si) - sn),化简得ans = sum(si) ^ 2 - sum(si ^2) .实际上,另外两个点选了(1,2)(2,1)对于集合{a,b,c}而言是一样的,所以方案数应该为ans/2

    而一个结点的子节点的总和为n-1。只要求sum(si^2)即可,由于是从某一点开始搜的,我们把这个点想成了根结点,实际上对于除这个点以外的其他点,除了按搜索顺序认为的子树外,剩下的结点是它的另一棵子树。

    代码实现:

    ll ans=0;

    int dfs(int x)//返回该结点的某棵子树的结点个数

    {

        vis[x] =1;

        ll sq=0;//”子树”结点个数的平方和

        int tot =0;//”子树”结点个数的总和

        for(int i=0; i<g[x].size(); ++i)

        {

            if(!vis[g[x][i]])

            {

                int son = dfs(g[x][i]);//子结点的个数

                tot += son;

                sq += (ll)son*son;

            }

        }

        sq += (ll)(n-tot-1)*(n-tot-1);

        ans += ((ll)(n-1)*(n-1) - sq)/2;

        return tot+1;//当自己作为子树时,还要加上自己,所以加1.

    }

    另一种解法:

    直接考虑顺序计数,有方案数ans =s1*(s2+s3+````+sn)+s2*(s3+s4+```sn) +```` + s[n-1]*sn. 则有设sum[i]为前i项和,则有ans = s1*(n-1-sum[1]) + s2*(n-1-sum[2]) +````+s[n-1]*(n-1-sum[n-1]) + sn *0.

    代码实现:

    ll ans;

    int dfs(int x)

    {

        vis[x] =1;

        int tmp=0;

        for(int i=0; i<g[x].size(); ++i)

        {

            if(!vis[g[x][i]])

            {

                int son = dfs(g[x][i]);//当前分支的儿子个数

                tmp += son;//已经求出的儿子个数,相当于sum[i]

                ans += (ll)(n-1-tmp)*son;

            }

        }

        return tmp+1;

    }

    然后此题的正解就是C(n,3)- ans.C(n,3)表示从n个点中选3个点,C(n,3) = n*(n-1)*(n-2)/6

    贴代码1:

     1 #pragma comment(linker, "/STACK:16777216")
     2 #include<cstdio>
     3 #include<vector>
     4 #define N 100010
     5 using namespace std;
     6 typedef long long int ll;
     7 vector<int> g[N];
     8 bool vis[N];
     9 int n;
    10 ll ans;
    11 int dfs(int x)
    12 {
    13     vis[x] =1;
    14     ll sq=0;//子节点个数的平方和
    15     int tot =0;//子节点个数之和
    16     for(int i=0; i<g[x].size(); ++i)
    17     {
    18         if(!vis[g[x][i]])
    19         {
    20             int son = dfs(g[x][i]);//子节点的个数
    21             tot += son;
    22             sq += (ll)son*son;
    23         }
    24     }
    25     sq += (ll)(n-tot-1)*(n-tot-1);
    26     ans += ((ll)(n-1)*(n-1) - sq)/2;
    27     return tot+1;
    28 }
    29 int main()
    30 {
    31     freopen("1010.in","r",stdin);
    32     while(~scanf("%d",&n))
    33     {
    34         for(int i=1; i<=n; ++i)
    35         {
    36             g[i].clear();
    37             vis[i] =0;
    38         }
    39         for(int i=1; i<n; ++i)
    40         {
    41             int u,v;
    42             scanf("%d%d",&u,&v);
    43             g[u].push_back(v);
    44             g[v].push_back(u);
    45         }
    46         ans =0;
    47         dfs(1);
    48         ll sum = (ll)n*(n-1)*(n-2)/6;
    49         printf("%I64d
    ",sum-ans);
    50     }
    51     return 0;
    52 }
    View Code

    贴代码2:

     1 #pragma comment(linker, "/STACK:16777216")
     2 #include<cstdio>
     3 #include<vector>
     4 #define N 100010
     5 using namespace std;
     6 typedef long long int ll;
     7 vector<int> g[N];
     8 bool vis[N];
     9 int n;
    10 ll ans;
    11 int dfs(int x)
    12 {
    13     vis[x] =1;
    14     int tmp=0;
    15     for(int i=0; i<g[x].size(); ++i)
    16     {
    17         if(!vis[g[x][i]])
    18         {
    19             int son = dfs(g[x][i]);//当前分支的儿子个数
    20             tmp += son;//已经求出的儿子个数
    21             ans += (ll)(n-1-tmp)*son;
    22         }
    23     }
    24     return tmp+1;
    25 }
    26 int main()
    27 {
    28 //    freopen("1010.in","r",stdin);
    29     while(~scanf("%d",&n))
    30     {
    31         for(int i=1; i<=n; ++i)
    32         {
    33             g[i].clear();
    34             vis[i] =0;
    35         }
    36         for(int i=1; i<n; ++i)
    37         {
    38             int u,v;
    39             scanf("%d%d",&u,&v);
    40             g[u].push_back(v);
    41             g[v].push_back(u);
    42         }
    43         ans =0;
    44         dfs(1);
    45         ll sum = (ll)n*(n-1)*(n-2)/6;
    46         printf("%I64d
    ",sum-ans);
    47     }
    48     return 0;
    49 }
    View Code
  • 相关阅读:
    驱动-问题解决
    Broadcast-广播的接收
    menu-代码添加以及add方法参数意义
    ubuntu-12.04工作区内容变换所属工作区
    $.ajax(),传参要用data
    阻止浏览器默认的事件冒泡行为
    form提交跳转问题
    根据已知值,选中selec中的选项
    option click事件
    按回车键确定
  • 原文地址:https://www.cnblogs.com/allh123/p/3281235.html
Copyright © 2020-2023  润新知