[BZOJ3683]Falsita

[BZOJ3683]Falsita

题目大意:

一个\(n(n\le3\times10^5)\)个结点的树,每个结点有一个权值\(w_i\)\(m(m\le3\times10^5)\)次操作,操作包含以下\(3\)种:

  1. 将结点\(u\)的权值加上\(d\)
  2. 将以\(u\)为根的子树中的每一个结点加上\(d\)
  3. 询问任取一个以\(u\)为LCA的点对\((x,y)\)\(w_x+w_y\)的期望值。

思路:

首先可以用一遍树形DP求出不考虑修改的答案\(ans_i\)

对于操作\(1\),会对\(u\)本身的答案产生\(d\times(size[u]-1)\)的贡献。对\(par[u]\)到根上每个结点\(x\)的贡献为\(d\times(size[x]-size[y])\),其中\(y\)\(x\)在链上的子结点。

对于操作\(2\),会对\(u\)子树内每个结点产生\(2d\times pair[x]\)的贡献,其中\(pair[u]\)为以\(u\)为LCA的点对数。对到根的链上结点贡献为\(d\times(size[x]-size[y])\times size[u]\)

由于对于每个结点,\((size[x]-size[y])\)\(pair[x]\)都是固定的,因此对于链上修改和子树修改,我们只需要维护\(d\)即可。这可以用树链剖分+线段树实现。

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

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
	register char ch;
	register bool neg=false;
	while(!isdigit(ch=getchar())) neg|=ch=='-';
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return neg?-x:x;
}
inline char getalpha() {
	register char ch;
	while(!isalpha(ch=getchar()));
	return ch;
}
typedef long long int64;
const int N=3e5+1;
int par[N],w[N],size[N],dep[N],son[N],dfn[N],top[N];
int64 pair[N],ans[N],sum[N];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
	e[u].push_back(v);
}
void dfs(const int &x,const int &par) {
	size[x]=1;
	sum[x]=w[x];
	dep[x]=dep[par]+1;
	for(unsigned i=0;i<e[x].size();i++) {
		const int &y=e[x][i];
		dfs(y,x);
		sum[x]+=sum[y];
		pair[x]+=1ll*size[x]*size[y];
		size[x]+=size[y];
		if(size[y]>size[son[x]]) {
			son[x]=y;
		}
	}
	ans[x]=1ll*w[x]*(size[x]-1);
	for(register unsigned i=0;i<e[x].size();i++) {
		const int &y=e[x][i];
		ans[x]+=1ll*sum[y]*(size[x]-size[y]);
	}
}
void dfs(const int &x) {
	dfn[x]=++dfn[0];
	top[x]=x==son[par[x]]?top[par[x]]:x;
	if(son[x]) dfs(son[x]);
	for(unsigned i=0;i<e[x].size();i++) {
		const int &y=e[x][i];
		if(y==son[x]) continue;
		dfs(y);
	}
}
class SegmentTree {
	#define _left <<1
	#define _right <<1|1
	#define mid ((b+e)>>1)
	private:
		int64 val[N<<2];
	public:
		void add(const int &p,const int &b,const int &e,const int &l,const int &r,const int64 &d) {
			if(b==l&&e==r) {
				val[p]+=d;
				return;
			}
			if(l<=mid) add(p _left,b,mid,l,std::min(mid,r),d);
			if(r>mid) add(p _right,mid+1,e,std::max(mid+1,l),r,d);
		}
		int64 query(const int &p,const int &b,const int &e,const int &x) const {
			int64 ret=val[p];
			if(b==e) return ret;
			if(x<=mid) ret+=query(p _left,b,mid,x);
			if(x>mid) ret+=query(p _right,mid+1,e,x);
			return ret;
		}
	#undef _left
	#undef _right
	#undef mid
};
SegmentTree t1,t2;
inline void modify0(int x,const int64 &d) {
	//链上修改
	while(x) {
		if(x!=top[x]) t1.add(1,1,dfn[0],dfn[top[x]],dfn[par[x]],d);
		x=top[x];
		if(par[x]!=0) ans[par[x]]+=d*(size[par[x]]-size[x]);
		x=par[x];
	}
}
inline void modify1(int x,const int64 &d) {
	//单点修改
	ans[x]+=d*(size[x]-1);
	modify0(x,d);
}
inline void modify2(const int &x,const int64 &d) {
	//子树修改
	if(size[x]>1) t2.add(1,1,dfn[0],dfn[x],dfn[x]+size[x]-1,d);
	modify0(x,d*size[x]);
}
inline double query(const int &x) {
	return 1.*(ans[x]+1ll*t1.query(1,1,dfn[0],dfn[x])*(size[x]-size[son[x]])+2ll*t2.query(1,1,dfn[0],dfn[x])*pair[x])/pair[x];
}
int main() {
	const int n=getint(),m=getint();
	for(register int i=2;i<=n;i++) {
		add_edge(par[i]=getint(),i);
	}
	for(register int i=1;i<=n;i++) {
		w[i]=getint();
	}
	dfs(1,0);
	dfs(1);
	for(register int i=0;i<m;i++) {
		const char opt=getalpha();
		const int u=getint();
		if(opt=='S') modify1(u,getint());
		if(opt=='M') modify2(u,getint());
		if(opt=='Q') printf("%.6f\n",query(u));
	}
	return 0;
}
posted @ 2018-12-24 19:50  skylee03  阅读(171)  评论(0编辑  收藏  举报