CF960H Santa's Gift

洛谷

CF

注:为了避免混淆,修改了部分变量的名称

  • 给出 \(n\) 个点的有根树,\(m\) 种颜色,点 \(i\) 有颜色 \(a_i\),颜色 \(i\) 有权值 \(b_i\)。维护两种操作,共 \(q\) 次:
    • \(\texttt{1 }x\text{ }y\),将 \(a_x\leftarrow y\)

    • \(\texttt{2 }x\),从树中等概率选取一个点 \(i\),得到 \((S_i\times b_x-C)^2\) 的价值。求期望价值。其中 \(S_i\) 表示以 \(i\) 为根的子树中颜色为 \(x\) 的节点数。\(C\) 是给定的常数。

  • \(n,m\le 5\times 10^4\)

先写出答案的表达式:

\[\begin{aligned}\dfrac{\sum_{i=1}^n(S_i\times b_x-C)^2}{n}&=\dfrac{\sum_{i=1}^n(b_x^2S_i^2-2b_xCS_i+C^2)}{n}\\&=\dfrac{b_x^2\sum_{i=1}^n S_i^2-2b_xC\sum_{i=1}^nS_i+nC^2}{n}\end{aligned} \]

考虑维护每种颜色的 \(S_i\),套路树剖再动态开点线段树。操作 \(\texttt{1}\) 相当于对颜色 \(a_x\) 将根到 \(x\) 路径上的 \(S_i\)\(1\),对于颜色 \(y\) 将这些 \(S_i\)\(1\)。这个修改区间含根节点很友好,不需要正常跳链,直接从 \(x\) 跳到根就好了。

线段树的节点要维护平方和以及和,简单讲一下平方和的维护:

\[\begin{aligned}\sum\limits_{i=l}^r(x_i+v)^2&=\sum\limits_{i=l}^r(x_i^2+2vx_i+v^2)\\&=\sum\limits_{i=l}^rx_i^2+2v\sum\limits_{i=l}^rx_i+(r-l+1)v^2\end{aligned} \]

就是在原来平方和的基础上加上 \(2v\) 倍的原来的和,再加上 \((r-l+1)v^2\)。由于是动态开点线段树不方便 pushdown,所以写了标记永久化。

对于 \(\texttt{2}\) 操作,查询区间为 \([1,n]\) 也很友好,直接用那种颜色线段树的根的信息就可以了。

时空复杂度均为 \(\mathcal{O}(m\log^2n)\),可以接受。

评测记录

$\color{orange}\bf点击查看代码$
//指针预警/cf。
#include<bits/stdc++.h>
#define ls x->lc
#define rs x->rc
#define int long long
using namespace std;
const int N=5e4+5;
int n,m,q,c,a[N],b[N],f[N],sz[N],d[N],t[N],h[N],dfn[N],cnt;
vector<int>g[N];
struct node{
	int sum,sq,add;
	node*lc,*rc;
	node(){
		sum=sq=add=0;
		lc=rc=NULL;
	}
}*rt[N];
void dfs1(int u){
	sz[u]=1;
	for(int v:g[u]){
		d[v]=d[u]+1;
		dfs1(v);
		sz[u]+=sz[v];
	}
}
void dfs2(int u){
	for(int v:g[u]){
		if((sz[v]<<1ll)>sz[u]){
			t[h[u]=v]=t[u];
		}else{
			t[v]=v;
		}
		dfs2(v);
	}
}
void dfs3(int u){
	dfn[u]=++cnt;
	if(h[u]){
		dfs3(h[u]);
	}
	for(int v:g[u]){
		if(v^h[u]){
			dfs3(v);
		}
	}
}
void modify(node*&x,int l,int r,int ql,int qr,int k){
	if(x==NULL){
		x=new node;
	}
	if(ql<=l&&r<=qr){
		x->add+=k;
		x->sq+=2*k*x->sum+(r-l+1)*k*k;
		x->sum+=(r-l+1)*k; 
	}else{
		int mid=(l+r)>>1ll;
		if(ql<=mid){
			modify(ls,l,mid,ql,qr,k);
		}
		if(qr>mid){
			modify(rs,mid+1,r,ql,qr,k);
		}
		x->sum=x->sq=0;
		if(ls!=NULL){
			x->sum+=ls->sum;
			x->sq+=ls->sq;
		}
		if(rs!=NULL){
			x->sum+=rs->sum;
			x->sq+=rs->sq;
		}
		x->sq+=2*x->add*x->sum+(r-l+1)*x->add*x->add;
		x->sum+=(r-l+1)*x->add;
	}
}
void chain(int k,int x,int v){
	while(x){
		modify(rt[k],1,n,dfn[t[x]],dfn[x],v);
		x=f[t[x]];
	}
}
signed main(){
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
	cin>>n>>m>>q>>c;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	for(int i=2;i<=n;++i){
		cin>>f[i];
		g[f[i]].emplace_back(i);
	}
	for(int i=1;i<=m;++i){
        rt[i]=NULL;
		cin>>b[i];
	}
	dfs1(1);
	dfs2(t[1]=1);
	dfs3(1);
	for(int i=1;i<=n;++i){
		chain(a[i],i,1);
	}
	for(int op,x,y;q--;){
		cin>>op>>x;
		if(op&1ll){
			cin>>y;
			chain(a[x],x,-1);
			chain(a[x]=y,x,1);
		}else{
			if(rt[x]==NULL){//无这种颜色。
				cout<<c*c<<'\n';
			}else{
				cout<<fixed<<setprecision(12)<<(1.0*b[x]*b[x]*rt[x]->sq-1.0*2*b[x]*c*rt[x]->sum+n*c*c)/n<<'\n';
			}
		}
	}
}
posted @ 2023-07-01 10:58  lzyqwq  阅读(18)  评论(0)    收藏  举报