1 条题解
-
0
自动搬运
来自洛谷,原作者为

Wen_kr
谢谢你。搬运于
2025-08-24 22:04:30,当前版本为作者最后更新于2018-12-03 09:53:35,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
很有意思的一道题,看到还没有人发布题解我就发了...
ps : PoPoQQQ dalao Orz
涉及 Link 和 Cut,我们考虑使用 LCT。
对于一条路径:
我们定义 为这条路径上第 个点的点权 (题目上的美丽程度)
那么设 表示这条路径的期望,那么 $exp = \frac{a_1*1*n+a_2*2*(n-1)+a_3*3*(n-2)+\dots+a_n*n*1}{C_n^2}$
(洛咕不能用 cfrac 字体有点小,见谅)
这个式子怎么得来的呢?
我们首先考虑子路径条数,显然这个数字等于
对于第 个点的权值 ,它显然会被 条路径覆盖(从 及 的左边选择左端点,从 及 的右边选择右端点)。
于是第 个点最后产生的贡献就是
这些贡献累加起来就成了我们的分子。
分母我们在 之后只需要 即可简单地算出,我们考虑在 每个节点中维护分子。
怎样进行维护呢,我们考虑一条跨过根节点的路径长这样:
a_1 --- a_2 --- a_3 --- a_4(根节点) --- a_5 --- a_6
左子树 右子树
此时根节点部分维护的分子为
左子树维护的分子为
左子树对当前节点的贡献应当为:
考虑两者做差,得到:
观察发现,外面的这个 恰好等于右子树的大小 + 1
我们设 $lsum = a_1 * 1 + a_2 * 2 + a_3 * 3 + \dots + a_n * n$
则左子树对答案的贡献为
同理,我们设
那么右子树对答案的贡献为
还有根节点对答案的贡献,这个很好计算,就是:
根节点的期望就可以计算了: $exp_{rt} = exp_{lc}+exp_{rc}+val_{rt}*(sz_{lc}+1)*(sz_{rc}+1)+$
也很好维护。
我们设 表示当前子树内所有节点的权值和,那么
$lsum = lsum_{lc} + lsum_{rc} + sum_{rc} * (sz_{lc} + 1) + val_{rt} * (sz_{lc}+1)$
$rsum = rsum_{rc} + rsum_{lc} + sum_{lc} * (sz_{rc} + 1) + val_{rt} * (sz_{rc}+1)$
这样,我们就解决了这个问题的一半:区间
我们现在考虑更新。
假设我们给某个节点的子树的所有节点权值加上
那么显然地,
$lsum_{rt} = lsum_{rt} + p * \frac{sz_{rt}*(sz_{rt}+1)}{2}$
与 相同。
然而 就很蛋疼了。
我们设
则$exp_{rt} = exp_{rt} + p * (1 * n + 2 * (n - 1) + \dots + n * 1)$
考虑重写 后面那一部分,我们能得到:
$(1 + 2 + 3 + \dots + n) * n - (1 * 2 + 2 * 3 + \dots + (n - 1) * n)$
前一部分很好化简,后面那一部分可以写成:
根据小学奥数,这后半个式子整个等于:
$\frac{n * (n + 1)}{2} * n - \frac{1}{6}*(n-1)*n*(2n-1)-\frac{n*(n-1)}{2}$
然后开始化简,步骤如下:
$$\frac{n^3+n^2}{2}-\frac{2n^3-2n^2-n^2+n}{6}-\frac{n^2-n}{2} $$这样我们就得到了更新的方法。
另外的,这道题 必须当场 ,否则会出问题(因为我们需要调用 和
最后,代码:
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <algorithm> using namespace std; int son[50050][2],fa[50050],sz[50050]; long long lsum[50050],val[50050],rsum[50050],EXP[50050],sum[50050],upd_tag[50050]; int rev_tag[50050]; void Push_Up(int rt) { sum[rt] = val[rt] + sum[son[rt][0]] + sum[son[rt][1]]; sz[rt] = 1 + sz[son[rt][0]] + sz[son[rt][1]]; lsum[rt] = lsum[son[rt][0]] + val[rt] * (sz[son[rt][0]] + 1) + lsum[son[rt][1]] + sum[son[rt][1]] * (sz[son[rt][0]] + 1); rsum[rt] = rsum[son[rt][1]] + val[rt] * (sz[son[rt][1]] + 1) + rsum[son[rt][0]] + sum[son[rt][0]] * (sz[son[rt][1]] + 1); EXP[rt] = EXP[son[rt][0]] + EXP[son[rt][1]] + val[rt] * (sz[son[rt][0]] + 1) * (sz[son[rt][1]] + 1) + lsum[son[rt][0]] * (sz[son[rt][1]] + 1) + rsum[son[rt][1]] * (sz[son[rt][0]] + 1); } void Rev(int rt) { swap(son[rt][0],son[rt][1]); swap(lsum[rt],rsum[rt]); rev_tag[rt] ^= 1; } void Add(int rt,int valx) { long long val1 = (sz[rt] * 1ll * (sz[rt] + 1)) / 2,val2 = (sz[rt] * 1ll * (sz[rt] + 1) * (sz[rt] + 2)) / 6; sum[rt] += valx * 1ll * sz[rt]; val[rt] += valx; lsum[rt] += val1 * valx; rsum[rt] += val1 * valx; EXP[rt] += valx * val2; upd_tag[rt] += valx; } void Push_Down(int rt) { if(rev_tag[rt]) { Rev(son[rt][0]); Rev(son[rt][1]); rev_tag[rt] = 0; } if(upd_tag[rt]) { Add(son[rt][0],upd_tag[rt]); Add(son[rt][1],upd_tag[rt]); upd_tag[rt] = 0; } } void Down(int rt) { if(fa[rt]) Down(fa[rt]); Push_Down(rt); } bool is_root(int rt) { return (son[fa[rt]][0] != rt && son[fa[rt]][1] != rt) || rt == 0; } void rotate(int rt) { int f = fa[rt],g = fa[f]; int way = son[f][1] == rt; if(!is_root(f)) son[g][son[g][1] == f] = rt; fa[rt] = g; son[f][way] = son[rt][way ^ 1]; if(son[rt][way ^ 1]) fa[son[rt][way ^ 1]] = f; son[rt][way ^ 1] = f; fa[f] = rt; Push_Up(f); Push_Up(rt); } void splay(int rt) { Down(rt); while(!is_root(rt)) { int f = fa[rt],g = fa[f]; if(!is_root(f)) (son[f][1] == rt) ^ (son[g][1] == rt) ? rotate(rt) : rotate(f); rotate(rt); } } void Access(int u) { for(int v = 0;u;v = u,u = fa[u]) { splay(u); son[u][1] = v; Push_Up(u); } } void make_root(int u) { Access(u); splay(u); Rev(u); } int find_root(int u) { Access(u); splay(u); while(son[u][0]) u = son[u][0]; return u; } long long split(int u,int v) { if(find_root(u) != find_root(v)) return -1; make_root(u); Access(v); splay(v); return EXP[v]; } void Update(int u,int v,int val) { if(find_root(u) != find_root(v)) return ; make_root(u); Access(v); splay(v); Add(v,val); } void Link(int u,int v) { if(find_root(u) == find_root(v)) return ; make_root(u); fa[u] = v; } void Cut(int u,int v) { if(find_root(u) != find_root(v)) return ; make_root(u); Access(v); splay(v); if(son[u][1] || son[v][0] != u) return; son[v][0] = fa[u] = 0; Push_Up(v); } long long gcd(long long a,long long b) { return b == 0 ? a : gcd(b,a % b); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i = 1;i <= n; ++ i) scanf("%lld",&val[i]),Push_Up(i); for(int i = 1;i < n; ++ i) { int u,v; scanf("%d%d",&u,&v); Link(u,v); } for(int i = 1;i <= m; ++ i) { int op,u,v;scanf("%d%d%d",&op,&u,&v); int w; if(op == 3) scanf("%d",&w); if(op == 1) Cut(u,v); else if(op == 2) Link(u,v); else if(op == 3) Update(u,v,w); else { long long tmp = split(u,v); if(tmp == -1) printf("-1\n"); else { long long csz = sz[v] * (sz[v] + 1) / 2; long long gcdx = gcd(csz,tmp); printf("%lld/%lld\n",tmp/gcdx,csz/gcdx); } } } }
- 1
信息
- ID
- 3730
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 上传者