很典型的按照边考虑贡献的题。

题目描述

小A居住的城市可以认为由n个街区组成。街区从1到n依次标号街区与街区之间由街道相连,每个街区都可以通过若干条街道到达任意一个街区,共有n-1条街道。其中标号为i的街区居住了i名居民。居民会去拜访别人,但是要花费dis(u,v)的过路费,u是他所在的城市,v是他拜访的人所在的城市。你需要求出,所有人都拜访其他人一次花费的过路费之和。

输入格式

第一行一个整数nn接下来n-1行,每行2个整数n−1n−1个整数描述n-1条街道

输出格式

一个整数,表示总花费之和

样例输入

5

1 2

2 3

2 4

1 5

样例输出

184

数据规模与约定

对于30%的数据,满足n≤200n≤200

对于60%的数据,满足n≤3000n≤3000

对于100%的数据,满足n≤1000000n≤1000000


题目分析

是一道典型的按边统计答案的题。但为什么我又没想出来啊。

题目求的是∑u∑v∗dis(u,v),那么来考虑一下问题的瓶颈在哪里。

按照定义直接做

首先是按照定义直接做的想法。

那么统计枚举所有点对,是 O(n^2) 的,预处理 dis(u,v) 有 O(n^3) 的floyd;还有 O(n^2) 的做 n 次dfs。

然而这个方向的做法空间复杂度是肯定要 O(n^2) 的,而且统计枚举的复杂度也难以改进。

考试时候就是吊死在这颗树上没出来了……

分边考虑贡献

统计时候不要那么“直接”,而是把整个答案分部分来考虑。

对于每一个点,与之相关的答案是 i*∑dis[i] 。于是我们发现最后的答案是只与 sum_{dis_i} 有关的。也就是说,对于点 x ,如果预处理了以它为根的 dis[] ,那么其贡献就是可以 O(1) 求出的。因此,解题瓶颈从处理点对的 dis[u][v] 变为了转移 dis[i] 。

对此,Cptraser表示有一种神奇的“平衡移动”方法。

这里(1,2)这条边是正在枚举的边。我们现在要做的是快速将 ∑dis[](以1为根) 转为 ∑dis[](以2为根) 。

图画出来后就很显然了。有$\sum_{newDis[]} \qquad \quad =\sum_{dis[]} \quad +tot_v-2*size[v]$ 。其中 size[v] 表示以 v 为根子树大小。当然这里所谓的子树大小是要提前人为确定一个根节点的。

这般把答案分部分之后,我们就会惊喜地发现复杂度降为$O(n+m)$了。

 #include<bits/stdc++.h>
typedef long long ll;
const ll MO = 1e9+;
const int maxn = ;
const int maxm = ; int n;
int edges[maxm],nxt[maxm],head[maxn],edgeTot;
ll tmp,ans,sum,dis[maxn],size[maxn]; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch = getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch = getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
void addedge(int u, int v)
{
edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
}
void dfs1(int x, int fa)
{
size[x] = x;
for (int i=head[x]; i!=-; i=nxt[i])
if (edges[i]!=fa)
dis[edges[i]] = dis[x]+, dfs1(edges[i], x), size[x] += size[edges[i]];
}
void dfs2(int x, int fa)
{
ll cnt = ;
for (int i=head[x]; i!=-; i=nxt[i])
if (edges[i]!=fa){
int v = edges[i];
cnt = (sum-2ll*size[v])%MO;
tmp = (tmp+cnt)%MO;
ans = (ans+tmp*v%MO)%MO;
dfs2(v, x);
tmp = (tmp-cnt+MO)%MO;
}
}
ll qmi(ll a, ll b)
{
ll ret = ;
while (b)
{
if (b&) ret = ret*a%MO;
a = a*a%MO, b >>= ;
}
return ret;
}
int main()
{
memset(head, -, sizeof head);
// freopen("city.in","r",stdin);
// freopen("city.out","w",stdout);
n = read();
for (int i=; i<n; i++) addedge(read(), read());
dfs1(, );
for (int i=; i<=n; i++) tmp += 1ll*i*dis[i];
ans = tmp, sum = 1ll*(n+)*n/;
dfs2(, );
printf("%lld\n",ans*qmi(, MO-)%MO);
return ;
}

END

【树形dp】7.14城市的更多相关文章

  1. CF 219D Choosing Capital for Treeland 树形DP 好题

    一个国家,有n座城市,编号为1~n,有n-1条有向边 如果不考虑边的有向性,这n个城市刚好构成一棵树 现在国王要在这n个城市中选择一个作为首都 要求:从首都可以到达这个国家的任何一个城市(边是有向的) ...

  2. Codeforces Beta Round #14 (Div. 2) D. Two Paths 树形dp

    D. Two Paths 题目连接: http://codeforces.com/contest/14/problem/D Description As you know, Bob's brother ...

  3. 洛谷 P1453 城市环路 ( 基环树树形dp )

    题目链接 题目背景 一座城市,往往会被人们划分为几个区域,例如住宅区.商业区.工业区等等.B市就被分为了以下的两个区域--城市中心和城市郊区.在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市 ...

  4. 【牛客】乃爱与城市拥挤程度 — 树形dp,up and down

    我太难了 这题做得我要死了,来来回回写了大概八九个小时 错误的原因要么是快速幂写错(一生之敌,要么是忘取模爆\(longlong\)变负数\(QAQ\) \(update\) \(2019.11.13 ...

  5. 2018.09.14 洛谷P3931 SAC E#1 - 一道难题 Tree(树形dp)

    传送门 简单dp题. f[i]表示以i为根的子树被割掉的最小值. 那么有: f[i]=min(∑vf[v],dist(i,fa))" role="presentation" ...

  6. Codeforces Beta Round #14 (Div. 2) Two Paths (树形DP)

    Two Paths time limit per test 2 seconds memory limit per test 64 megabytes input standard input outp ...

  7. BZOJ4890 [Tjoi2017]城市 【树形dp】

    题目链接 BZOJ4890 题解 枚举断开哪一条边,然后对剩余的两棵树分别做一遍换根法树形dp 需要求出每个点到树中其它点距离的最大值\(f[i]\)和次大值\(g[i]\)[用以辅助换根计算最大值] ...

  8. BZOJ 4890: [Tjoi2017]城市 树形dp

    标签:树形dp,枚举,树的直径 一上来看到这个题就慌了,只想到了 $O(n^3)$ 的做法. 碰到这种题时要一步一步冷静地去分析,观察数据范围. 首先,$n\leqslant 5000$,所以可以先 ...

  9. 【NOIP2016提高A组集训第14场11.12】随机游走——期望+树形DP

    好久没有写过题解了--现在感觉以前的题解弱爆了,还有这么多访问量-- 没有考虑别人的感受,没有放描述.代码,题解也写得歪歪扭扭. 并且我要强烈谴责某些写题解的代码不打注释的人,像天书那样,不是写给普通 ...

  10. 树形DP小结

    树形DP1.简介:树是一种数据结构,因为树具有良好的子结构,而恰好DP是从最优子问题更新而来,那么在树上做DP操作就是从树的根节点开始深搜(也就是记忆化搜索),保存每一步的最优结果.tips:树的遍历 ...

随机推荐

  1. 在项目中移除CocoaPods

    如果你觉得CocoaPods让你的项目出现了问题,不好用甚至是恶心,想将其从项目中彻底移除,也有方法: 1.删除工程文件夹下的Podfile.Podfile.lock和Pods文件夹. 2.删除xcw ...

  2. Macbook上sublime的C++11弥补bits/stdc++.h的配置

    如果在windows配置过的话这次会容易很多.相关博客很多了,我这里保存一下我借鉴并成功的配置: 关于自己build的C++,文件类型为sublime-build,直接扔在它给出的user文件夹即可, ...

  3. 阿里云-域名免费申请ssl证书过程

    1.运行证书服务docker docker run --entrypoint="/bin/sh" -it --name certbotsh certbot/certbot:late ...

  4. c/c++学习系列之内存对齐

    1.C++内存对齐规则 每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数).程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你 ...

  5. Django框架和前端的的基本结合

    1 昨日回顾 a socket b 路由关系 c 模板字符串替换(模板语言) 主流web框架总结: django a用别人的 b自己写的 c自己写的 flask a用别人的 b自己写的 c用别人的(j ...

  6. 【实用】Html5实现文件异步上传

    1 简介 开发文件上传功能从来不是一件愉快的事,异步上传更是如此,使用过iframe和Flash的上传方案,也都感觉十分的别扭.本文简要简绍利用Html5的FormData实现文件的异步上传,还可以实 ...

  7. CF1066B Heaters

    思路: 从左向右贪心选择能覆盖当前位置的最靠右的那个heater即可,和poj radar installation类似. 实现: #include <iostream> #include ...

  8. Yii 2.0排序功能的使用

    在Yii2.0项目的实际开发中,经常会遇到使用Yii2.0自带的排序功能.下面是排序功能的具体使用方法. 一.设置排序规则 注意引入Sort类,如:use yii\data\Sort; // 设置排序 ...

  9. “chm 已取消到该网页的导航”解决方案

    1. 右键单击该 CHM 文件,然后单击“属性”. 2. 单击“取消阻止”或者“解除锁定”. 3. 双击此 .chm 文件以打开此文件.

  10. java httpclient 跳过证书验证

    import java.io.IOException;import java.net.InetAddress;import java.net.Socket;import java.net.Unknow ...