bzoj3683: Falsita
题解: 我们考虑不待修改的情况 sigma(sz[x]-sz[y])*(sum[y])+(sz[x]-1)*v[x] 直接一遍dfs处理即可 分母部分同样mu[x]=sz[x]^2-1-sigma(sz[y]^2) 然后我们考虑单点修改的情况 对于当前这个点x 它对应的ans[x]+=delet*(sz[x]-1) 对于这个节点到根路径上的点 delet*(sz[y]-sz[x])(x是路径上的点且是y的儿子节点) 那么我们考虑把这一段用重链剖开 对于重链重链交接的点特殊处理 其他的用两个树状数组维护k*delet*(sz[x]-sz[son[x])(k为被修改的节点个数)和区间加的这段区间delet*mu[x]即可
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=3e5+10; const double eps=1e-8; #define ll long long using namespace std; struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } ll ans[MAXN],mu[MAXN],sum[MAXN],a[MAXN]; int dep[MAXN],fa[MAXN],son[MAXN],num[MAXN],n; void dfs(int x,int pre,int deep){ num[x]=1;dep[x]=deep+1;fa[x]=pre; link(x){ if(j->t!=pre){ dfs(j->t,x,deep+1); num[x]+=num[j->t];sum[x]+=sum[j->t];ans[x]-=1ll*num[j->t]*sum[j->t]; if(son[x]==-1||num[son[x]]<num[j->t])son[x]=j->t; mu[x]-=1ll*num[j->t]*num[j->t]; } } mu[x]+=1ll*num[x]*num[x]-1; ans[x]+=1ll*num[x]*sum[x]-a[x]; } int p[MAXN],tp[MAXN],fp[MAXN],cnt; void dfs1(int x,int td){ p[x]=++cnt;fp[p[x]]=x;tp[x]=td; if(son[x]!=-1)dfs1(son[x],td); link(x){ if(j->t!=fa[x]&&j->t!=son[x])dfs1(j->t,j->t); } } ll sum1[MAXN],sum2[MAXN]; int get_id(int x){return x&(-x);} void update1(int x,ll key){ for(int i=x;i<=n+1;i+=get_id(i))sum1[i]+=key; } ll Sum1(int x){ ll ans1=0; for(int i=x;i>0;i-=get_id(i))ans1+=sum1[i]; return ans1; } void slove(int u,int v,ll key){ if(son[fa[u]]!=u){ ans[fa[u]]+=1ll*key*(num[fa[u]]-num[u]); u=fa[u]; } u=fa[u]; if(!u)return ; int uu=tp[u];int vv=tp[v],last=0; while(uu!=vv){ update1(p[uu],key);update1(p[u]+1,-key); u=fa[uu];last=uu;uu=tp[u]; if(uu!=vv){ ans[u]+=1ll*key*(num[u]-num[last]); u=fa[u];uu=tp[u]; } } if(last){ ans[u]+=1ll*key*(num[u]-num[last]); u=fa[u]; } if(u){ update1(p[1],key);update1(p[u]+1,-key); } } void update2(int x,ll key){ for(int i=x;i<=n+1;i+=get_id(i))sum2[i]+=key; } ll Sum2(int x){ ll ans2=0; for(int i=x;i>0;i-=get_id(i))ans2+=sum2[i]; return ans2; } int main(){ n=read();int m=read(); int u,v;char ch;ll key; inc(i,2,n)u=read(),add(u,i); inc(i,1,n)a[i]=sum[i]=read(),son[i]=-1; dfs(1,0,0);dfs1(1,1); while(m--){ scanf(" %c",&ch); if(ch=='S'){ v=read();key=read(); ans[v]+=1ll*(num[v]-1)*key; slove(v,1,key); } else if(ch=='M'){ v=read();key=read(); update2(p[v],key);update2(p[v]+num[v],-key); slove(v,1,1ll*num[v]*key); } else{ v=read(); ll ans1=Sum1(p[v]);ans1=1ll*ans1*(num[v]-num[son[v]]); ll ans2=Sum2(p[v]);ans2=1ll*ans2*mu[v]; ll ans3=2*(ans[v]+ans2+ans1); printf("%.6f\n",ans3*1.0/mu[v]); } } return 0; }
3683: Falsita
Time Limit: 20 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 340 Solved: 137
[Submit][Status][Discuss]
Description
描述
到海边了呢......
如果没有那次选择,现在是不是会好些呢......
都过去了。
仰望着星空,迎面吹过一阵阵海风,倚靠着护栏,Fine 在海边静静地伫立着,在一个个无际的长夜后,Fine 终于放下了往事的痛楚,得到了治愈。
但是作为 Fine 的另一重人格的 Falsita 就没那么幸运了。她仍然被各种繁忙的事务困扰着。
虽然同在一副躯体中,Fine 与 Falsita 的精神世界却相差甚远,Fine 可以轻易地构造出幻梦时,Falsita 却只能停留在现实的痛楚中。
但是为了生活需要,她们还是需要经常达成共识。
让我们形式化的描述一下吧。
她们所在的精神世界是一棵以 1 号节点为根的树,每个树上的节点 u 都有一个权值Wu,她们每个人分别都在一个节点上,达成共识的方法就是两个人都到达一个共识节点(即到达它们的最近公共祖先)。
一个点 u 与另外一个点 v 之间想要达到共识需要花费的代价为Wu+Wv。
有时两人的精神有所触动时,有时点的权值会改变成某个数,有时以某个点的子树中的所有点的权值会加上某个数。
Falsita 和 Fine 经常需要达成共识,每一次询问,已知达成的共识节点,求她们花费的期望代价。
Input
输入共 m + 3 行。
第一行两个整数 n, m ,表示节点个数和操作数。
第二行 n - 1 个整数Pi,表示节点 i ( i = 2 . . . n ) 的父亲节点的编号。
第三行 n 个整数Wi。
接下来 m 行,每行表示一个操作。
1. S u delta 表示将节点 u 的权值加上 delta 。
2. M u delta 表示将以节点 u 为根的子树中的所有节点的权值加上 delta。
3. Q u 表示询问共识节点为 u 时的答案。
询问保证 u 不是叶子节点。
Output
对于每组询问,输出答案,答案精确到小数点后 6 位。
你的程序输出的答案需要与标准答案之差不超过10^(-5)。
Sample Input
1 2 2
0 -6 3 0
S 2 -5
M 3 8
S 1 -1
M 4 7
M 3 2
Q 1