牛半仙的妹子树

2023-10-19 07:59
文章标签 妹子 半仙

本文主要是介绍牛半仙的妹子树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

牛半仙的妹子树 ⁡ \operatorname{牛半仙的妹子树}

题目链接: nowcoder 212916 ⁡ \operatorname{nowcoder\ 212916} nowcoder 212916

关于这场比赛

——>点我可以查看其它题目(目录)<——

到牛客看

——>点我跳转<——

题目

牛半仙有 n n_{} n 个妹子,她们所在的位置组成一棵树,相邻两个妹子的距离为 1 1_{} 1
m m_{} m 个妹子具有超能力,她们会影响到其他妹子。
离所有具有超能力的妹子的最短距离在 [ l , r ] [l,r]_{} [l,r] 间的妹子会受到影响,会具有旪超能力。
这些具有能力的妹子共同形成了一个磁场。对于一个位置,一个具有超能力的妹子为其增加的磁场强度为妹子到这个位置的距离的平方,一个具有旪超能力的妹子为其增加的磁场强度为妹子到这个位置的距离。
现在牛半仙想知道一个位置的磁场强度有多大。
因为牛半仙对妹子们特别关心,所以他有 k k_{} k 个询问。

输入

第一行 5 5_{} 5 个正整数,代表 n , m , k , l , r n,m,k,l,r_{} n,m,k,l,r
2 2_{} 2 行到第 n n_{} n 行每行 2 2_{} 2 个正整数 u i , v i u_i,v_i ui,vi ,代表树中存在边 ( u i , v i ) (u_i,v_i) (ui,vi)
n + 1 n+1_{} n+1 行有 m m_{} m 个正整数,代表这些妹子有超能力。
n + 2 n+2_{} n+2 行有 k k_{} k 个正整数,代表询问的妹子。对于每个询问,输出一行,代表该询问的答案。

输出

对于每个询问,输出一行,代表该询问的答案。

样例输入

11 2 4 2 2
1 2
1 3
2 4
2 5
3 6
3 7
5 8
5 9
9 10
9 11
3 9
1 6 9 11

样例输出

14
34
20
32

样例解释

在这里插入图片描述

图中红点为超能力,绿点为旪超能力

1 1 1 号点权值为 1 2 + 3 2 + 1 + 3 = 14 1^2+3^2+1+3=14 12+32+1+3=14
6 6 6 号点权值为 1 2 + 5 2 + 3 + 5 = 34 1^2+5^2+3+5=34 12+52+3+5=34
9 9 9 号点权值为 0 2 + 4 2 + 2 + 2 = 20 0^2+4^2+2+2=20 02+42+2+2=20
11 11 11 号点权值为 1 2 + 5 2 + 3 + 3 = 32 1^2+5^2+3+3=32 12+52+3+3=32

数据范围

对于 5 % 5\% 5% 的数据: n , m , k ≤ 100 n,m,k≤100 n,m,k100
对于 20 % 20\% 20% 的数据: n , m , k ≤ 2000 n,m,k\le2000 n,m,k2000
对于另 20 % 20\% 20% 的数据: u i = i , v i = i + 1 u_i=i,v_i=i+1 ui=i,vi=i+1
对于另 20 % 20\% 20% 的数据: u i = 1 u_i=1 ui=1
对于 100 % 100\% 100% 的数据: n , m , k ≤ 5 × 1 0 5 n,m,k\le5\times10^5 n,m,k5×105

思路

这道题是一道很烦的题目,用了 dp、换根等等操作。

首先,我们可以求出某一个点是否是超能力点或者旪超能力点。
我这里用 s u p [ i ] sup[i] sup[i] p s u p [ i ] psup[i] psup[i] 表示。
那超能力点很好搞,至于旪超能力点,我们可以通过bfs,从每一个超能力点出发,算出每一个点距离最近的超能力点的距离 d i s [ i ] dis[i] dis[i]
(要用bfs因为这样可以保证第一次找到的时候距离最短)
那如果这个距离在 [ l , r ] [l,r] [l,r] 之间,那这个点就是旪超能力点。

接着,我们可以通过dfs的树形dp求出一一为根的时候的一些数值:
s u m [ i ] sum[i] sum[i] 表示点 i i i 与它的子树中有多少个点是超能力点
p s u m [ i ] psum[i] psum[i] 表示点 i i i 与它的子树中有多少个点是旪超能力点
(这两个很好搞,就直接等于它每个子树的个数之和,如果自己本身是就 + 1 +1 +1
d d i s [ i ] ddis[i] ddis[i] 表示点 i i i 与它的子树所有超能力点对答案的贡献( i i i 就是要到的位置)
p d i s [ i ] pdis[i] pdis[i] 表示点 i i i 与它的子树所有旪超能力点对答案的贡献

p d i s [ i ] pdis[i] pdis[i] 还好说,就直接每一个子树的 p d i s [ i ] pdis[i] pdis[i] 加每一个子树的旪超能力点数。(因为每一个旪超能力点距离要到的位置都加了 1 1 1,那合在一起就是 1 ∗ p s u m [ i ] 1*psum[i] 1psum[i] p s u m [ i ] psum[i] psum[i]
但是 d d i s [ i ] ddis[i] ddis[i] 怎么的每一个数都要平方,怎么办呢?
先搞点简单的, d i s [ i ] dis[i] dis[i] 表示点 i i i 与它的子树所有超能力点到 i i i 的距离和。
那求这个的方式就和 p d i s [ i ] pdis[i] pdis[i] 一样,只是把 p d i s [ i ] pdis[i] pdis[i] 换成 d i s [ i ] dis[i] dis[i] p s u m [ i ] psum[i] psum[i] 换成 s u m [ i ] sum[i] sum[i]

那接着我们来看如何求 d d i s [ i ] ddis[i] ddis[i]
设原来一个在子树 x x x 中的超能力点距离子树 x x x 的根是 y y y 的距离为 z z z,那么上移到 i i i,距离加 1 1 1,贡献就变成了 ( z + 1 ) 2 (z+1)^2 (z+1)2,化简得到 z 2 + 2 z + 1 z^2+2z+1 z2+2z+1
枚举每一个子树的根 y y y,那就是所有 d d i s [ y ] + 2 ∗ d i s [ y ] + s u m [ y ] ddis[y]+2*dis[y]+sum[y] ddis[y]+2dis[y]+sum[y] 加起来。

但是这只是一个点为要到的位置,如果正常枚举每一个点,就会超时。
这个时候,我们就要用到换根。(就是枚举儿子,把它拎到上面做根,然后维护儿子的各项值值)

那我们来看如何维护上面的值,设原来的根是 x x x,现在要把根变成它的一个儿子 i i i

s u m [ ] sum[] sum[] 好弄,就 s u m [ i ] = s u m [ x ] sum[i]=sum[x] sum[i]=sum[x]
p s u m [ ] psum[] psum[] 也是一样: p s u m [ i ] = p s u m [ x ] psum[i]=psum[x] psum[i]=psum[x]

维护答案的思路大致是这样的:
在这里插入图片描述

p d i s [ ] pdis[] pdis[] 还好,就 p d i s [ i ] = p d i s [ i ] + ( ( p d i s [ x ] − ( p d i s [ i ] + p s u m [ i ] ) ) + ( p s u m [ x ] − p s u m [ i ] ) ) pdis[i] = pdis[i]+((pdis[x] - (pdis[i] + psum[i])) + (psum[x] - psum[i])) pdis[i]=pdis[i]+((pdis[x](pdis[i]+psum[i]))+(psum[x]psum[i]))
p d i s [ i ] pdis[i] pdis[i] 就是蓝色部分的贡献)
p d i s [ i ] + p s u m [ i ] pdis[i]+psum[i] pdis[i]+psum[i] 表示新做根的点及下面的子树原来对答案的贡献)
(那 p d i s [ x ] − ( p d i s [ i ] + p s u m [ i ] ) pdis[x] - (pdis[i] + psum[i]) pdis[x](pdis[i]+psum[i]) 就是不是那棵子树的点原来对答案的贡献)
p s u m [ x ] − p s u m [ i ] psum[x] - psum[i] psum[x]psum[i] 就是不在蓝色框框的点的个数,也就是这些点走一个距离增加的贡献。因为之前只只用到 x x x,这一次要多走到 i i i,所以多走了一个距离)

d i s [ ] dis[] dis[] 同理,就只要改一下变量名即可。

接着我们来看最烦的 d d i s [ ] ddis[] ddis[],先把式子写出来:
d i s s [ i ] = d d i s [ i ] + ( ( d d i s [ x ] − ( d d i s [ i ] + 2 ∗ d i s [ i ] + s u m [ i ] ) ) + 2 ∗ ( d i s [ x ] − ( d i s [ i ] + s u m [ i ] ) ) + ( s u m [ x ] − s u m [ i ] ) ) diss[i]=ddis[i] + ((ddis[x] - (ddis[i] + 2 * dis[i] + sum[i])) + 2 * (dis[x] - (dis[i] + sum[i])) + (sum[x] - sum[i])) diss[i]=ddis[i]+((ddis[x](ddis[i]+2dis[i]+sum[i]))+2(dis[x](dis[i]+sum[i]))+(sum[x]sum[i]))
d d i s [ i ] ddis[i] ddis[i] 就是蓝色部分的贡献)
d d i s [ i ] + ( 2 ∗ d i s [ i ] + s u m [ i ] ) ddis[i] + (2 * dis[i] + sum[i]) ddis[i]+(2dis[i]+sum[i]) 就是新做根的点及下面的子树原来对答案的贡献)
(那 d d i s [ x ] − ( d d i s [ i ] + 2 ∗ d i s [ i ] + s u m [ i ] ) ddis[x] - (ddis[i] + 2 * dis[i] + sum[i]) ddis[x](ddis[i]+2dis[i]+sum[i]) 就是不是那棵子树的点原来对答案的贡献)
2 ∗ ( d i s [ x ] − ( d i s [ i ] + s u m [ i ] ) ) + ( s u m [ x ] − s u m [ i ] ) 2 * (dis[x] - (dis[i] + sum[i])) + (sum[x] - sum[i]) 2(dis[x](dis[i]+sum[i]))+(sum[x]sum[i]) 就是不是蓝色部分的点走一个距离增加的贡献:由 x 2 x^2 x2 变成 x 2 + 2 x + 1 x^2+2x+1 x2+2x+1,多了 2 x + 1 2x+1 2x+1

写这么多,给个三连吧QAQ

比赛时

看到题目,想了半天都想不出比较优的解,就只能唯唯诺诺的打一个只有 5 5 5 分的 Floyed。

在这里插入图片描述

代码

#include<queue>
#include<cstdio>
#include<cstring>
#define ll long longusing namespace std;struct node {ll to, nxt;
}e[5000001];
struct bfsu {ll x, size;
}noww;
ll n, m, k, l, r, x, y, le[500001], KK, sup[500001], psup[500001], in[500001], re;
ll sum[500001], psum[500001], dis[500001], pdis[500001], ddis[500001];
queue <bfsu> q;
char c;ll read() {re = 0;c = getchar();while (c < '0' || c > '9') c = getchar();while (c >= '0' && c <= '9') {re = re * 10 + c - '0';c = getchar();}return re;
}void add(ll x, ll y) {e[++KK] = (node){y, le[x]}; le[x] = KK;e[++KK] = (node){x, le[y]}; le[y] = KK;
}void bfs() {//bfs求出每个点距离最近的超能力点的距离while (!q.empty()) {noww = q.front();q.pop();dis[noww.x] = noww.size;for (ll i = le[noww.x]; i; i = e[i].nxt)if (!in[e[i].to]) {in[e[i].to] = 1;q.push((bfsu){e[i].to, noww.size + 1});}}
}void dfsget(ll now, ll fa) {//通过dp求出以1位根时的对应值sum[now] = sup[now];psum[now] = psup[now];for (ll i = le[now]; i; i = e[i].nxt)if (e[i].to != fa) {dfsget(e[i].to, now);sum[now] += sum[e[i].to];psum[now] += psum[e[i].to];ddis[now] += ddis[e[i].to] + 2 * dis[e[i].to] + sum[e[i].to];dis[now] += dis[e[i].to] + sum[e[i].to];pdis[now] += pdis[e[i].to] + psum[e[i].to];}
}void dfschan(ll now, ll fa) {//换根for (ll i = le[now]; i; i = e[i].nxt)if (e[i].to != fa) {ddis[e[i].to] += (ddis[now] - (ddis[e[i].to] + 2 * dis[e[i].to] + sum[e[i].to])) + 2 * (dis[now] - (dis[e[i].to] + sum[e[i].to])) + (sum[now] - sum[e[i].to]);dis[e[i].to] += (dis[now] - (dis[e[i].to] + sum[e[i].to])) + (sum[now] - sum[e[i].to]);pdis[e[i].to] += (pdis[now] - (pdis[e[i].to] + psum[e[i].to])) + (psum[now] - psum[e[i].to]);sum[e[i].to] = sum[now];psum[e[i].to] = psum[now];dfschan(e[i].to, now);}
}int main() {n = read();m = read();k = read();l = read();r = read();for (ll i = 1; i < n; i++) {x = read();y = read();add(x, y);}for (ll i = 1; i <= m; i++) {x = read();sup[x] = 1;q.push((bfsu){x, 0});in[x] = 1;}bfs();//bfs求出每个点距离最近的超能力点的距离for (ll i = 1; i <= n; i++)if (dis[i] >= l && dis[i] <= r)psup[i] = 1;//找到旪超能力点memset(dis, 0, sizeof(dis));dfsget(1, 0);//通过dp求出以1位根时的对应值dfschan(1, 0);//换根for (ll i = 1; i <= k; i++) {x = read();printf("%lld\n", ddis[x] + pdis[x]);}return 0;
} 

这篇关于牛半仙的妹子树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/238483

相关文章

一枚程序员的爱情观,被一个我认为的浙江妹子一个渣渣,让我提高了我的认识,瞬间感觉人生在世,一定要去做些为往事开太平,立功立德立言,做一番成绩

我能做到疼你爱你,热恋时腻歪你,平淡期守护你。对爱感情专一、认真,坦诚相待,一辈子忠心爱你。不会朝三暮四。奔着结婚的目的好好疼你爱你,容忍你的小脾气,赚钱都给你花。同时我也要求你能够体谅我,爱我,陪我白头到老,共同经营爱情,不能随便的说分手,忠于爱情,不允许做出出轨的举动和背叛的事情。恋爱过程中,要互相理解,信任,宽容。对待恋爱要严肃认真,要对伴侣专一。有一个积极、乐观的心态。不要因为恐惧,

【NOIP2016提高A组模拟9.9】运输妹子

Description 小轩轩是一位非同一般的的大农(lao)场(si)主(ji),他有一大片非同一般的农田,并且坐落在一条公路旁(可以认为是数轴),在他的农田里种的东西也非同一般——不是什么水稻小麦,而是妹子。 在小轩轩的细心培育下,他的大片农田都要结出妹子啦!但是他的农田分布实在是太广阔了,他担心自己的妹子会令路过的人想入非非,于是他想要把所有农田上的妹子都集中到一个仓库里面,贮存起来。可

看我用 Python 一秒发送数百份邮件,让财务部妹子追着喊 666!

“ 阅读本文大概需要 8 分钟。 ” 需求描述 最近公司要发奖金,需要财务部妹子给每个员工发一封邮件,现在全公司 10 个部门每个人的奖金情况已经计算好了,并根据部门分别制作了 10 张表格: 每个奖金表格内容大致如下: 同时有一份 Excel 文件邮件地址.xlsx,里面有各部门负责人的邮箱: 首先第一个需求很简单:给各部门负责人发送相应部门的奖金附件。 这个需求不同于群发,只要把所有

Python3网络爬虫(三):Python3使用Cookie-模拟登陆获取妹子联系方式

转载请注明作者和出处:http://blog.csdn.net/c406495762 运行平台:Windows Python版本:Python3.x IDE:Sublime text3 一、为什么要使用Cookie     Cookie,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。      比如说有些网站需要登录后才能访问

使用3Dmax做一个妹子 一 基本操作方法

首先可以从这里创建一个立方体,在图中通过鼠标设置立方体的长宽和大小。 这个可以设置选中物体并移动。 按下M键打开材质编辑器,位置如图所示,拖动一个标准色过去: 右边属性框里可以改改名字: 右键物体,转换为可编辑多边形。 然后右键->Freeze Selection,再进行右键,选择: 可以看到变为下面的样子: 再去右面属性里往下拉,取消选择: 就能得到硬

[牛客2020第4场]牛半仙的妹子序列

牛半仙的妹子序列 题解 《关于我因过于这道题卡常而将其称为"贞卡常"的这件事》 场上被卡常了,下来加了几个优化就卡过去了。 这道题应该很容易看出是一个dp,40pts的 O ( n 2 ) O(n^2) O(n2)的dp式子应该是很好想的。 我们定义 d p i dp_{i} dpi​为在只关注第 i i i个数到第 n n n个数之间的序列构成合法序列的方案数。 容易得到方程式 d

[bzoj3744]Gty的妹子序列 解题报告

比较显然的做法是用bit维护做到 O(nlog−−−√n) O(n\sqrt \log n)。 但是。。作为一名理论计算机科学家傻逼,我们需要 O(nn√) O(n\sqrt n)的做法,注意到如果我们把 (i,ai) (i,a_i)看成点,实际上要求 O(1) O(1)询问一个矩形内点的个数,这个显然可以用可持久化分块来搞,维护每个块内的前缀和和所有块的前缀和——但是空间复杂度是 3nn√ 3

[bzoj3720]Gty的妹子树 解题报告

大概看了一眼网上的题解,跟块爷一样都写的会被卡的分块。(反正块爷出的题也不会卡自己。。) 这里说一种比较科学的做法。就是用块链维护dfs序。 维护每个节点按dfs序是在哪个块的哪个位置,对每个块维护块中节点的最浅深度、它的下一个块是哪个块,块中节点按dfs序排序的序列,按权值排序的序列。 一开始的时候每B个分一块,最后一块节点数 ≤B \le B。查询的时候在两边的块暴力,在中间的块里二分,

selenium攻占煎蛋妹子图

python 版本 3.5 依赖库: seleniumbeautifulsoap4lxmlrequests 使用selenium的原因是 requests库 在实际操作的时候,发现请求返回的内容里面并没有图片的链接: <p> <img src="//img.jandan.net/img/blank.gif" onload="jandan_load_img(this)"/> <spa

html标签元素类型,一个前端妹子的面试笔记

计算机网络篇 HTTP HTTP 报文结构是怎样的?HTTP有哪些请求方法?GET 和 POST 有什么区别?如何理解 URI?如何理解 HTTP 状态码?简要概括一下 HTTP 的特点和缺点?对 Accept 系列字段了解多少?对于定长和不定长的数据,HTTP 是怎么传输的?HTTP 如何处理大文件的传输?HTTP 中如何处理表单数据的提交?HTTP1.1 如何解决 HTTP 的队头阻塞问题