题目大意:
  给你一颗$n(n\le5000)$个点的树,选3个点使得它们两两距离相等,问共有几种选法。

思路:
  首先我们不难发现一个性质:对于每3个符合条件的点,我们总能找到一个点使得这个点到那3个点距离相等。
  我们不妨称之为“中转点”。
  显然答案就是对于每个中转点,不同子树中到这个点距离相等的三元点对的数量。
  我们可以先枚举每个点作为中转点的情况。
  暴力求出以这个点的每个子结点为根的子树,不同深度的结点的数量(显然深度就是到这个中转点的距离)。
  我们可以用calc[i][j]表示对于当前中转点,来自j个不同子树的深度为i的结点共有多少种不同的组合。
  转移方程为calc[i][j]+=calc[i][j-1]*cnt[i]。

  1. #include<cstdio>
  2. #include<cctype>
  3. #include<vector>
  4. #include<cstring>
  5. typedef long long int64;
  6. inline int getint() {
  7. register char ch;
  8. while(!isdigit(ch=getchar()));
  9. register int x=ch^'';
  10. while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
  11. return x;
  12. }
  13. const int N=;
  14. std::vector<int> e[N];
  15. inline void add_edge(const int &u,const int &v) {
  16. e[u].push_back(v);
  17. e[v].push_back(u);
  18. }
  19. int n,cnt[N];
  20. int64 calc[N][];
  21. void dfs(const int &x,const int &par,const int &dep) {
  22. cnt[dep]++;
  23. for(unsigned i=;i<e[x].size();i++) {
  24. const int &y=e[x][i];
  25. if(y==par) continue;
  26. dfs(y,x,dep+);
  27. }
  28. }
  29. int main() {
  30. n=getint();
  31. for(register int i=;i<n;i++) {
  32. add_edge(getint(),getint());
  33. }
  34. int64 ans=;
  35. for(register int x=;x<=n;x++) {
  36. memset(calc,,sizeof calc);
  37. for(register int i=;i<=n;i++) calc[i][]=;
  38. for(register unsigned i=;i<e[x].size();i++) {
  39. memset(cnt,,sizeof cnt);
  40. const int &y=e[x][i];
  41. dfs(y,x,);
  42. for(register int j=;j;j--) {
  43. for(register int i=;i<=n;i++) {
  44. calc[i][j]+=calc[i][j-]*cnt[i];
  45. }
  46. }
  47. }
  48. for(register int i=;i<=n;i++) {
  49. ans+=calc[i][];
  50. }
  51. }
  52. printf("%lld\n",ans);
  53. return ;
  54. }   现在考虑当$n\le10^5$的情况。

  考虑$n\le10^5$的情况。
  $f[i][j]$标示以$i$为根的子树中,与$i$距离为$j$的点数。$g[i][j]$标示以$i$为根的子树中,与$i$距离为$j$的点对数。则不难想到一种$O(n^2)$的转移:
  $$
  \begin{align*}
  &g[x][i-1]+=g[y][i]\\
  &g[x][i+1]+=f[x][i+1]\times f[y][i]\\
  &f[x][i+1]+=f[y][i]
  \end{align*}
  $$
  ​边界为$f[x][0]=1$。
  考虑优化这个转移,不难发现,若$y$是$x$枚举到的第一个子结点,则转移时只进行第一、第三个转移。因此我们可以考虑通过指针来实现,免去转移的过程。
  将原树进行长链剖分,对于重边直接修改指针,对于轻边暴力转移,可以证明这样是$O(n)$的。

  1. #include<list>
  2. #include<cstdio>
  3. #include<cctype>
  4. typedef long long int64;
  5. inline int getint() {
  6. register char ch;
  7. while(!isdigit(ch=getchar()));
  8. register int x=ch^'';
  9. while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
  10. return x;
  11. }
  12. const int N=;
  13. std::list<int> e[N];
  14. int dep[N],bot[N];
  15. int64 mem[N*],ans,*f[N],*g[N],*ptr=mem;
  16. inline void add_edge(const int &u,const int &v) {
  17. e[u].push_back(v);
  18. e[v].push_back(u);
  19. }
  20. void dfs(const int &x,const int &par) {
  21. dep[bot[x]=x]=dep[par]+;
  22. for(std::list<int>::iterator i=e[x].begin();i!=e[x].end();i++) {
  23. const int &y=*i;
  24. if(y==par) continue;
  25. dfs(y,x);
  26. if(dep[bot[y]]>dep[bot[x]]) bot[x]=bot[y];
  27. }
  28. for(register std::list<int>::iterator i=e[x].begin();i!=e[x].end();i++) {
  29. const int &y=*i;
  30. if(y==par||(bot[y]==bot[x]&&x!=)) continue;
  31. f[bot[y]]=ptr+=dep[bot[y]]-dep[x]+;
  32. g[bot[y]]=++ptr;
  33. ptr+=(dep[bot[y]]-dep[x])*+;
  34. }
  35. }
  36. void dp(const int &x,const int &par) {
  37. for(std::list<int>::iterator i=e[x].begin();i!=e[x].end();i++) {
  38. const int &y=*i;
  39. if(y==par) continue;
  40. dp(y,x);
  41. if(bot[y]==bot[x]) {
  42. f[x]=f[y]-;
  43. g[x]=g[y]+;
  44. }
  45. }
  46. ans+=g[x][];
  47. f[x][]=;
  48. for(register std::list<int>::iterator i=e[x].begin();i!=e[x].end();i++) {
  49. const int &y=*i;
  50. if(y==par||bot[y]==bot[x]) continue;
  51. for(register int i=;i<=dep[bot[y]]-dep[x];i++) {
  52. ans+=f[x][i-]*g[y][i]+g[x][i+]*f[y][i];
  53. }
  54. for(register int i=;i<=dep[bot[y]]-dep[x];i++) {
  55. g[x][i-]+=g[y][i];
  56. g[x][i+]+=f[x][i+]*f[y][i];
  57. f[x][i+]+=f[y][i];
  58. }
  59. }
  60. }
  61. int main() {
  62. const int n=getint();
  63. for(register int i=;i<n;i++) {
  64. add_edge(getint(),getint());
  65. }
  66. dfs(,);
  67. dp(,);
  68. printf("%lld\n",ans);
  69. return ;
  70. }

[POI2014]Hotel的更多相关文章

  1. BZOJ3522: [Poi2014]Hotel

    3522: [Poi2014]Hotel Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 195  Solved: 85[Submit][Status] ...

  2. 3522: [Poi2014]Hotel( 树形dp )

    枚举中点x( 即选出的三个点 a , b , c 满足 dist( x , a ) = dist( x , b ) = dist( x , c ) ) , 然后以 x 为 root 做 dfs , 显 ...

  3. 3522: [Poi2014]Hotel

    3522: [Poi2014]Hotel Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 253  Solved: 117[Submit][Status ...

  4. 【刷题】BZOJ 4543 [POI2014]Hotel加强版

    Description 同OJ3522 数据范围:n<=100000 Solution dp的设计见[刷题]BZOJ 3522 [Poi2014]Hotel 然后发现dp的第二维与深度有关,于是 ...

  5. 4543: [POI2014]Hotel加强版

    4543: [POI2014]Hotel加强版 链接 分析: f[u][i]表示子树u内,距离u为i的点的个数,g[u][i]表示在子树u内,已经选了两个深度一样的点,还需要在距离u为i的一个点作为第 ...

  6. BZOJ4543 POI2014 Hotel加强版 【长链剖分】【DP】*

    BZOJ4543 POI2014 Hotel加强版 Description 同OJ3522 数据范围:n<=100000 Sample Input 7 1 2 5 7 2 5 2 3 5 6 4 ...

  7. 【BZOJ4543】[POI2014]Hotel加强版 长链剖分+DP

    [BZOJ4543][POI2014]Hotel加强版 Description 同OJ3522数据范围:n<=100000 Sample Input 7 1 2 5 7 2 5 2 3 5 6 ...

  8. 【BZOJ3522】[Poi2014]Hotel 树形DP

    [BZOJ3522][Poi2014]Hotel Description 有一个树形结构的宾馆,n个房间,n-1条无向边,每条边的长度相同,任意两个房间可以相互到达.吉丽要给他的三个妹子各开(一个)房 ...

  9. bzoj4543[POI2014]Hotel

    题目链接 bzoj4543 [POI2014]Hotel 题解 这不是裸地点分嘛 ,我真傻,真的 n^2 这不是是sb题,~滑稽 ~ 枚举点转换为无根树,暴力子树中点的深度 计数转移 令a b c d ...

  10. bzoj4543 [POI2014]Hotel加强版 长链剖分+树形DP

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4543 题解 这道题的弱化版 bzoj3522 [POI2014]Hotel 的做法有好几种吧. ...

随机推荐

  1. Codeforces Round #535 (Div. 3) 题解

    Codeforces Round #535 (Div. 3) 题目总链接:https://codeforces.com/contest/1108 太懒了啊~好久之前的我现在才更新,赶紧补上吧,不能漏掉 ...

  2. bzoj 5099 [POI2018]Pionek 计算几何 极角排序

    [POI2018]Pionek Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 269  Solved: 80[Submit][Status][Disc ...

  3. 两数之和 [ leetcode ]

    原题地址:https://leetcode-cn.com/articles/two-sum/ 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元 ...

  4. 【Atcoder】ARC082 E - ConvexScore

    [算法]计算几何 [题意]给定平面直角坐标系上的若干个点,任意选点连成凸多边形,凸多边形的价值定义为2^(n-|S|),其中n为凸多边形内部点数(含边界),|S|为顶点数,求总价值.n<=10^ ...

  5. 【STSRM12】夏令营

    [题意]n个数划分成k段,每段的价值为段内不同数字的数量,求最大总价值 [算法]DP+线段树 [题解] f[i][j]表示前i个数字划分成j段的最大价值. f[i][j]=max(f[k][j-1]+ ...

  6. 我在开发中所遇到的iOS7新特性以及iOS7与iOS6的适配问题总结

      ⓵UIImageView 1. // iOS7添加的对图像颜色处理的功能,过滤颜色的功能 2. _imageView.tintColor = [UIColor blueColor]; 3. //重 ...

  7. Kali 1.0 / 2.0 安装中文输入法(谷歌pinyin + 其他)

    1.kali默认是没有中午输入法的,需要自己安装一下 2.首先我们先获取root权限 dnt@HackerKali:~$ su密码: 3.安装中文输入法(apt-get 指令不会的同学可以学习一下基础 ...

  8. letsencrypt的证书转换上传到360网站防护方法

    命令:openssl 首先letsencrypt生成的证书在 letsencrypt/live/xxx.com/ 下,需要使用cert.pem和privkey.pem文件. 生成crt文件: open ...

  9. 中小型mysql数据库的备份与恢复

    #转载请联系 备份到桌面 cd /home/chichung/Desktop # 切换到桌面 mysqldump -u root -p db_jingdong>jd.sql # 重定向写入 jd ...

  10. 使用Bind服务配置DNS服务器

    bind是什么 bind是DNS服务器软件 ,他的服务名称是named 功能区分: 正向解析:根据主机名查找对应的IP地址 反向解析:根据IP地址查找对应的主机名(域名) 工作形式上区分: 主服务器: ...