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 Judge
Submit: 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

4 6
1 2 2
0 -6 3 0
S 2 -5
M 3 8
S 1 -1
M 4 7
M 3 2
Q 1

Sample Output

2.000000
posted @ 2018-11-06 01:53  wang9897  阅读(129)  评论(0编辑  收藏  举报