当前位置: 首页 > news >正文

[题解]P4116 Qtree3

P3925 aaa被续

题目描述没看懂(雾)

简单解释一下:

对于节点\(u\),将子树\(u\)中的权值从大到小排序,记“权值乘排名之和”为\(u\)的贡献。

输出总贡献。


下文定义\(w[u]\)\(u\)的权值,\(siz[u]\)为子树\(u\)的大小。

考虑每个节点对答案的贡献,看起来比较困难。

转而考虑每个权值对答案的贡献。

不难发现,如果将子树\(u\)中的节点按权值从大到小排序,那么这些点的排名依次是\(siz[u],siz[u]-1,\dots,1\)

所以我们按权值从大到小遍历每个节点。需要统计它的贡献的节点,就是它到根节点路径上的所有节点。

遍历这些节点,在\(v\)点累加的贡献是\(siz[v]\times w[u]\),并且将\(siz[v]\)减去\(1\)

这样,\(siz[v]\)表示的始终是\(u\)在子树\(v\)中的排名。因为我们的节点是按权值从大到小统计的。

因此我们需要支持的操作:

  • \(u\)到根节点路径上的\(siz\)统一减去\(1\)
  • 统计\(u\)到根节点的\(siz\)之和(将所求的和\(\times w[u]\)累入答案即可)。

可以用树剖实现。

时间复杂度\(O(n\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N=5e5+10,P=1e9+7;
int n,head[N],fa[N],dep[N],siz[N],son[N],top[N],dfn[N],tim,w[N],idx;
ll ans;
struct edge{int nxt,to;}e[N<<1];
void add(int u,int v){e[++idx]={head[u],v},head[u]=idx;}
inline int lb(int x){return x&-x;}
struct BIT{int s1[N],s2[N];void chp(int x,int v){for(int i=x;i<=n;i+=lb(i)) s1[i]+=v,s2[i]+=v*x;}void chr(int x,int y,int v){chp(x,v),chp(y+1,-v);}int query(int x){int ans=0;for(int i=x;i;i-=lb(i)) ans+=(x+1)*s1[i]-s2[i];return ans;}int query(int x,int y){return query(y)-query(x-1);}
}bit;
struct Node{int p,w;
}p[N];
void dfs1(int u){siz[u]=1,dep[u]=dep[fa[u]]+1;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(v==fa[u]) continue;fa[v]=u,dfs1(v),siz[u]+=siz[v];if(siz[v]>siz[son[u]]) son[u]=v;}
}
void dfs2(int u,int t){top[u]=t,dfn[u]=++tim,bit.chr(dfn[u],dfn[u],siz[u]);if(!son[u]) return;dfs2(son[u],t);for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(v!=fa[u]&&v!=son[u]) dfs2(v,v);}
}
void ch_chain(int u,int v,int iv){while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]]) swap(u,v);bit.chr(dfn[top[u]],dfn[u],iv);u=fa[top[u]];}if(dep[u]<dep[v]) swap(u,v);bit.chr(dfn[v],dfn[u],iv);
}
ll query(int u,int v){ll ans=0;while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]]) swap(u,v);ans+=bit.query(dfn[top[u]],dfn[u]);ans%=P;u=fa[top[u]];}if(dep[u]<dep[v]) swap(u,v);ans+=bit.query(dfn[v],dfn[u]);return ans%P;
}
signed main(){cin>>n;for(int i=1,u,v;i<n;i++) cin>>u>>v,add(u,v),add(v,u);for(int i=1;i<=n;i++) cin>>w[i],p[i]={i,w[i]};dfs1(1),dfs2(1,1);sort(p+1,p+1+n,[](Node a,Node b){return a.w>b.w;});for(int i=1;i<=n;i++){(ans+=query(1,p[i].p)%P*p[i].w)%=P;ch_chain(1,p[i].p,-1);}cout<<ans<<"\n";return 0;
}
http://www.njgz.com.cn/news/700.html

相关文章:

  • 第二十五天
  • JAVA语言学习总结(第26天)
  • 初遇前端
  • 【复习笔记】莫队
  • 初遇JDBC
  • vue3 pina使用
  • CobaltStrike流量分析
  • 【Nordic随笔】nRF54L15的引脚说明
  • CNVD-2024-15077 AJ-Report 认证绕过与远程代码执行漏洞 (复现)
  • Atcoder Beginner Contest 416
  • NCS添加.c.h文件
  • 明月直入,无心可猜
  • realtek网卡r8168如何强制设置1000M
  • mobaXterm免费版保存密码查询
  • 公司类型英文缩写
  • CVE-2020-17526 Apache Airflow 身份验证绕过漏洞 (复现)
  • Pwn2Own柏林2025次日战报:单日狂揽43.5万美元奖金,20个零日漏洞曝光
  • Day27
  • 猫树
  • 大道至简读后感
  • 一些感覺比較好的題目
  • 7.17XYD模拟赛
  • 如何把整套网站的源代码弄下来.250408
  • 牛客 周赛101 20250726
  • 人生的意义,就是没有意义.250421
  • 牛客2025多校 R3
  • 数论基础H
  • 推理大模型 vs 普通大模型:核心差异与国产代表产品
  • 【动态规划】树上连通块计数
  • Windows自带神器Robocopy一键备份文件文件夹,断点续传+多线程效率翻倍!.250429