【树链剖分+线段树】CF960H Santa's Gift

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/85838191

【题目】
原题地址
给定一棵 n n 个节点的有根树和一个参数 C C ,每个节点有一个编号 f i f_i ,范围为 1 m 1\sim m ,每种编号有一个权值 c i c_i ,有两种操作:

  • 修改一个点的编号
  • 给出 x x ,记 c n t i cnt_i 为树中节点 i i 子树中编号为 x x 的节点个数,求 i = 1 n ( c n t i c x C ) 2 n \frac {\sum_{i=1}^n(cnt_i\cdot c_x-C)^2} n

【解题思路】
我们拆开求的式子可以得到 c n t i 2 c x 2 2 c n t i c x C + C 2 \sum cnt_i^2c_x^2-2cnt_i\cdot c_x\cdot C+C^2
于是我们树剖,对每种编号建动态开点线段树然后维护一下几个值即可。

纯粹的码力练习。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef double db;
typedef long long ll;
const int N=1e5+10,M=N*150;
int n,m,Q,C;
int col[N];
ll cnt[N],cnt2[N],c[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

namespace Graph
{
	int tot,head[N];
	struct Tway{int v,nex;}e[N];
	void addedge(int u,int v){e[++tot]=(Tway){v,head[u]};head[u]=tot;}
}
using namespace Graph;

namespace Segment
{
	int rt[N];
	struct Segment
	{
		int sz,ls[M],rs[M];
		ll sum[M],tar[M];

		void pushup(int x){sum[x]=sum[ls[x]]+sum[rs[x]];}
		void pushdown(int x,int l)
		{
			if(!tar[x]) return;
			if(!ls[x]) ls[x]=++sz;
			if(!rs[x]) rs[x]=++sz;
			ll c=tar[x];tar[x]=0;
			sum[ls[x]]+=c*(l-(l>>1));
			sum[rs[x]]+=c*(l>>1);
			tar[ls[x]]+=c;tar[rs[x]]+=c;
		}
		void update(int &x,int l,int r,int L,int R,int c)
		{
			if(!x) x=++sz;
			if(L<=l && r<=R) 
			{
				sum[x]+=(ll)c*(r-l+1);tar[x]+=c;
				return;
			}
			pushdown(x,r-l+1);
			int mid=(l+r)>>1;
			if(L<=mid) update(ls[x],l,mid,L,R,c);
			if(R>mid) update(rs[x],mid+1,r,L,R,c);
			pushup(x);
		}
		ll query(int x,int l,int r,int L,int R)
		{
			if(!x) return 0;
			if(L<=l && r<=R) return sum[x];
			pushdown(x,r-l+1);
			int mid=(l+r)>>1;ll res=0;
			if(L<=mid) res+=query(ls[x],l,mid,L,R);
			if(R>mid) res+=query(rs[x],mid+1,r,L,R);
			return res;
		}
	}tr;
}
using namespace Segment;

namespace TrainCut
{
	int ind;
	int siz[N],dep[N],fa[N],son[N],top[N],pos[N];
	void dfs1(int x)
	{
		siz[x]=1;dep[x]=dep[fa[x]]+1;
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;dfs1(v);siz[x]+=siz[v];
			if(siz[v]>siz[son[x]]) son[x]=v;
		}
	}
	void dfs2(int x,int tp)
	{
		pos[x]=++ind;top[x]=tp;
		if(son[x]) dfs2(son[x],tp);
		for(int i=head[x];i;i=e[i].nex)
			if(e[i].v^son[x]) dfs2(e[i].v,e[i].v);
	}
	void update(int r,int x,int y)
	{
		for(;x;x=fa[top[x]]) 
			tr.update(rt[r],1,n,pos[top[x]],pos[x],y);
	}
	ll query(int r,int x)
	{
		ll res=0;
		for(;x;x=fa[top[x]])
			res+=tr.query(rt[r],1,n,pos[top[x]],pos[x]);
		return res;
	}
	void upval(int x,int y)
	{
		cnt[x]+=dep[y];
		cnt2[x]+=query(x,y)*2;cnt2[x]+=dep[y];
		update(rt[x],y,1);
	}
	void downval(int x,int y)
	{
		cnt[x]-=dep[y];
		update(x,y,-1);
		cnt2[x]-=query(rt[x],y)*2;cnt2[x]-=dep[y];
	}
}
using namespace TrainCut;

ll sqr(int x){return (ll)x*x;}
db calc(int x){return (db)(cnt2[x]*sqr(c[x])-2*cnt[x]*c[x]*C+n*sqr(C))/n;}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF960H.in","r",stdin);
	freopen("CF960H.out","w",stdout);
#endif
	n=read();m=read();Q=read();C=read();
	for(int i=1;i<=n;++i) col[i]=read();
	for(int i=2;i<=n;++i) fa[i]=read(),addedge(fa[i],i);
	for(int i=1;i<=m;++i) c[i]=read();
	dfs1(1);dfs2(1,1);
	for(int i=1;i<=m;++i) rt[i]=++tr.sz;
	for(int i=1;i<=n;++i) upval(col[i],i);
	while(Q--)
	{
		int op=read(),x,y;
		if(op&1)
		{
			x=read();y=read();
			downval(col[x],x);col[x]=y;upval(col[x],x);
		}
		else x=read(),printf("%.10lf\n",calc(x));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/85838191
今日推荐