• 杭电多校七 1003-Counting Stickmen(组合数学)


    题目链接:杭电多校7 - Virtual Judge

    题目:

     题意:上面标红的部分是一个人,也只有上面这种形状的才算是人,问给定的一棵树中有多少个类似上图的人。

    分析:我们能看到3-5这条边算是这个人的腰,因为一个人的腰是唯一的,所以我们可以枚举腰来统计人的个数。对于每一条边u-v,我们都可以假设u是连接腿的节点也可以假设v是连接腿的节点,把两种情况相加即可不重不漏地统计完所有的人。下面我以v作为连接腿的节点来阐述一下如何计算答案:

    首先要选择两条边与v有关的边作为腿,而且所选的边还不能包含u-v,那么我们可以用d[i]记录i的度,孩子节点数就是度-1,那么我们脚的选择方案就有(d[i]-1)*(d[i]-2)/2,下面我们来看一下手,手的选择方案计算起来就稍微麻烦一点,首先我们应该从连接头的节点u选择两个长度为2的链作为手,而且这两个长度为2的链要满足两个条件:

    (1)不能经过边u-v

    (2)不能含有公共部分

    我们先不考虑上面两个情况,就是说先看一下从u出发长度为2的链的总条数,其实这个就是u的孩子节点的所有孩子节点和(这里假设节点之间是有父子关系的),这个是比较容易理解的,那经过u-v的长度为2的链有多少呢?因为u-v长度已经是1了,所以我们只需要统计一下从v出发长度为1的且不包含u-v的链有多少就行了,其实也就是v的孩子节点的个数,那么我们用所有的长度为2的链减去经过u-v的长度为2的链就是所有的从u出发的且不经过u-v的长度为2的链的数目,不妨记为t,我们从中任选两个链作为手的方案数就是t*(t-1)/2,但是这样还有一点问题,就是说我们选的两条链虽然都不经过u-v,但是有可能经过同一条边:也就是两个手是这样的情况:

    把1-2-3和1-2-4分别看成两个手,这样显然是不符合题意的,我们需要减去这种情况,而这种情况的数目并不是很难计算,比如说j是u的一个孩子节点,那么从j的孩子节点中任选两个都会构成这种情况,所以我们要计算的就是u的所有孩子节点中会构成这种情况的总数目,这个也不难计算,就是C(cnt[j],2)其中j是u的孩子节点,cnt[j]是j的孩子节点,也就是d[j]-1,然后求一个和就行了,需要注意的一点是我们之前没有考虑经过u-v的边,所以这一次考虑u的孩子时也不能考虑v,而为了我们预处理的方便,我们需要先考虑所有孩子节点的边然后最后减去即可。所以我们需要预处理出来

    (代码说明:第一个是d[],第二个是f1[],第三个是f2[])

    这样我们就处理出来了腿的方案数和手的方案数,头的方案数就更简单了,直接是d[u]-3,因为我们只要选一个长度为1的链,且这条链不能和腰重合,也不能和手重合,所以就是d[u]-3,因为头和手和腰的选取是独立的,所以最后只需要取一个乘积,然后直接对每一条边进行统计答案即可。

    细节见代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. const int N=5e5+10,mod=998244353;
    11. int h[N],e[N*10],ne[N*10],idx;
    12. void add(int x,int y)
    13. {
    14. e[idx]=y;
    15. ne[idx]=h[x];
    16. h[x]=idx++;
    17. }
    18. int u[N],v[N];
    19. long long d[N],f1[N],f2[N];
    20. //d[i]存储第i个节点的度
    21. //f1[i]存储第i个节点的孩子节点的孩子节点数目和
    22. //f2[i]存储的是第i个节点的孩子节点中选两个孩子的方案数之和
    23. void dfs(int x,int fa)
    24. {
    25. if(fa!=-1)//防止fa为负数时进行数组索引造成越界
    26. {
    27. f1[x]=d[fa]-1;
    28. f2[x]=(d[fa]-1)*(d[fa]-2)/2%mod;
    29. }
    30. else f1[x]=f2[x]=0;
    31. for(int i=h[x];i!=-1;i=ne[i])
    32. {
    33. int j=e[i];
    34. if(j==fa) return ;
    35. dfs(j,x);
    36. f1[x]=(f1[x]+d[j]-1)%mod;
    37. f2[x]=(f2[x]+(d[j]-1)*(d[j]-2)/2%mod)%mod;
    38. }
    39. }
    40. int main()
    41. {
    42. int T;
    43. cin>>T;
    44. while(T--)
    45. {
    46. int n;
    47. scanf("%d",&n);
    48. idx=0;
    49. for(int i=1;i<=n;i++) h[i]=-1,d[i]=0;
    50. for(int i=1;i
    51. {
    52. scanf("%d%d",&u[i],&v[i]);
    53. add(u[i],v[i]);add(v[i],u[i]);
    54. d[u[i]]++;d[v[i]]++;
    55. }
    56. dfs(1,-1);
    57. long long ans=0;
    58. for(int i=1;i
    59. {
    60. long long tv=(d[v[i]]-1)*(d[v[i]]-2)/2%mod;//以v点作为腿的方案数
    61. long long tu=(d[u[i]]-1)*(d[u[i]]-2)/2%mod;//以u点作为腿的方案数
    62. long long sumu=(f1[u[i]]-(d[v[i]]-1))*(f1[u[i]]-d[v[i]])/2%mod;
    63. long long sumv=(f1[v[i]]-(d[u[i]]-1))*(f1[v[i]]-d[u[i]])/2%mod;
    64. ans=(ans+tv*(d[u[i]]-3)%mod*(sumu-(f2[u[i]]-tv)))%mod;
    65. ans=(ans+tu*(d[v[i]]-3)%mod*(sumv-(f2[v[i]]-tu)))%mod;
    66. }
    67. printf("%lld\n",(ans%mod+mod)%mod);
    68. }
    69. return 0;
    70. }

  • 相关阅读:
    leetcode646. 最长数对链(java)
    深入解析Python并发编程的多线程和异步编程
    6-3 pytorch使用GPU训练模型
    不堆概念、换个角度聊多线程并发编程
    【编程之路】Python编程实例解析
    Linux 命令(194)—— ethtool 命令
    【英语:发音基础】A5.基础词汇-基础名词
    JAVA学习笔记28(常用类)
    Windows11 手把手教授开放端口
    Java————方法重载
  • 原文地址:https://blog.csdn.net/AC__dream/article/details/126266992