CF960H 题解
题意简述
注:原题翻译有点小锅,题目中的
给出一棵
有两种操作共
-
,将 。 -
,从树中等概率选取一个点 ,得到 的价值。求期望价值。其中 表示以 为根的子树中颜色为 的节点数。 是给定的常数。
题目分析
一个比较裸的树剖,甚至只有路径修改单点查询。
先分析操作
再看操作
注意到颜色数不多,很容易想到重链剖分后直接维护
代码实现
#include<bits/stdc++.h> using namespace std; int n,m,q,op,x,y,f[50010],b[50010],a[50010],nd,num[50010]; int tot,hd[50010],nt[50010],v[50010]; int cnt,d[50010],top[50010],fa[50010],dfn[50010],son[50010],size[50010]; long long C; struct node { int ls,rs;//左右子结点编号。 long long sqsum,sum,tag;//平方和、和、懒标记。 }tr[16000010];//线段树结点结构体 void rd(int &x) { x=0; char c=getchar(); for(;c>'9'||c<'0';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) x=(x<<3)+(x<<1)+c-'0'; }//快读 void rd(long long &x) { x=0ll; char c=getchar(); for(;c>'9'||c<'0';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) x=(x<<3ll)+(x<<1ll)+c-'0'; }//快读 void add(int x,int y) { v[++tot]=y; nt[tot]=hd[x]; hd[x]=tot; }//建边 void dfs1(int x) { size[x]=1; d[x]=d[fa[x]]+1; for(int i=hd[x];i;i=nt[i]) { int y=v[i]; dfs1(y); size[x]+=size[y]; if(size[y]>size[son[x]]) son[x]=y; } }//第一遍 DFS,计算出结点深度 d、子树大小 size 并找到重儿子 son。 void dfs2(int x,int tp) { top[x]=tp; dfn[x]=++cnt; if(!son[x]) return; dfs2(son[x],tp); for(int i=hd[x];i;i=nt[i]) { int y=v[i]; if(y!=son[x]) dfs2(y,y); } }//第二遍 DFS,计算出重链顶 top、遍历顺序 dfn。 void pushup(int p) { tr[p].sqsum=tr[tr[p].ls].sqsum+tr[tr[p].rs].sqsum;//平方和更新 tr[p].sum=tr[tr[p].ls].sum+tr[tr[p].rs].sum;//和更新 }//用子结点更新父结点。 void addtag(int &p,long long val,int len) { if(!p) p=++nd;//原来没有就新建。 tr[p].sqsum+=2ll*tr[p].sum*val+1ll*len*val*val;//平方和更新 tr[p].sum+=1ll*len*val;//和更新 tr[p].tag+=val;//标记更新 }//打标记。 void pushdown(int &p,int len) { if(tr[p].tag) { addtag(tr[p].ls,tr[p].tag,(len+1)/2);//左儿子 addtag(tr[p].rs,tr[p].tag,len/2);//右儿子 tr[p].tag=0;//清空标记 } }//下传懒标记。 void change(int ql,int qr,int &p,int l,int r,long long val) //ql:当前结点左端点,qr:当前结点右端点,p:当前结点,l:询问左端点,r:询问右端点,val:更新的值。 { if(!p) p=++nd;//原来没有就新建。 if(ql>=l&&qr<=r)//全包含直接修改。 { addtag(p,val,qr-ql+1); return; } pushdown(p,qr-ql+1); int mid=ql+qr>>1; if(mid>=l) change(ql,mid,tr[p].ls,l,r,val);//左子结点 if(mid<r) change(mid+1,qr,tr[p].rs,l,r,val); pushup(p); }//线段树区间修改。 void change_to_root(int x,int col,long long val)//从 x 修改到根。 { while(top[x]!=1) { change(1,n,num[col],dfn[top[x]],dfn[x],val); x=fa[top[x]];//一条重链一条重链地改 } change(1,n,num[col],1,dfn[x],val); } int main() { rd(n),rd(m),rd(q),rd(C); for(int i=1;i<=n;i++) rd(a[i]); for(int i=2;i<=n;i++) rd(fa[i]),add(fa[i],i); for(int i=1;i<=m;i++) rd(b[i]); dfs1(1); dfs2(1,1); for(int i=1;i<=n;i++) change_to_root(i,a[i],1);//初始化 while(q--) { rd(op),rd(x); if(op==1) { rd(y); change_to_root(x,a[x],-1);//把原来颜色的删掉 1 a[x]=y; change_to_root(x,a[x],1);//新颜色加上 1 } else printf("%.10lf\n",(double)(b[x]*b[x]*tr[num[x]].sqsum-2*b[x]*C*tr[num[x]].sum)/n+C*C); } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性