ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

牛半仙的妹子树

2021-08-28 14:02:43  阅读:198  来源: 互联网

标签:半仙 int ll 超能力 wh fa numa 妹子


题面

牛半仙有n个妹子,她们所在的位置组成一棵树,相邻两个妹子的距离为1,m个妹子具有超能力,她们会影响到其他妹子。离所有具有超能力的妹子的最短距离在[l,r]间的妹子会受到影响,会具有旪超能力。

这些具有能力的妹子共同形成了一个磁场。对于一个位置,一个具有超能力的妹子为其增加的磁场强度为妹子到这个位置的距离的平方,一个具有旪超能力的妹子为其增加的磁场强度为妹子到这个位置的距离。

现在牛半仙想知道一个位置的磁场强度有多大。

因为牛半仙对妹子们特别关心,所以他有k个询问。

\(n,m,k\le 5\times10^5\)

解法

写了两道题了,也累了,写简单一点吧……

换根DP,理解了之后比前两道题都好写。

首先把具有旪超能力的节点找出来,这一点可以用广搜实现(毕竟是求每个点到超能力的点的最短距离,又是一棵树,完全可以用搜索做)。

然后就是换根DP的惯常操作(虽然换根DP的题我也没做几道)。

第一步,以某个点为根,求出每棵子树的dp值。由于超能力点和旪超能力点的贡献算法不同且互相独立,我们把它们分开算。

假如\(fb_i\)代表以i为根的子树中所有旪超能力的点对节点i的贡献,\(numb_i\)代表以i为根的子树中叶超节点的个数,那么\(fb_i=\sum {fb_j+numb_j}\),j为i的儿子。理由很简单,让所有的叶超节点势力范围都往上走了一格,那么它们每个点都贡献一个新的磁场,总共不就贡献了“数量”个磁场吗?

超能力的点稍微复杂。假如\(fa_i\)代表以i为根的子树种所有超能力点对i的贡献,\(numa_i\)还是代表数量,那么发现\(fa_i=\sum{(dis_j+1)^2}\),\(dis_j\)为超能力点到i某个儿子节点的最小值。拆开,可得\(fa_i=\sum{{dis_j}^2+2\times dis_j+1}\),\(\sum{{dis_j}^2}\)存在i的儿子节点们的fa中,\(\sum{2\times dis_j}\)可以维护,就相当于维护子树内所有超能力点到子树树根的距离之和,用和上一段话同样的方法即可,假如存于fa1中,那么,整理可得:\(fa_i=\sum{fa_k+2\times fa1_k+numa_k}\),k为i的孩子。

然后就可以计算出以1为根时所有子树的贡献情况。接下来是换根部分。

把你的父亲当成你的孩子(感觉这句话有点不太对),计算节点父亲的fa、fa1、fb、numa、numb,然后用这些值更新当前节点的这些数值即可。numa、numb的计算方式是总数减去以当前节点为根(1为根的意义下)的子树的数量,其它的就是父亲当前的值减去这个节点对于父亲贡献的值。然后dfs即可。

对于询问,由于不带修改,直接输出\(fa+fb\)即可。

复杂度\(O(N^2)\)。

#include<cstdio>
#include<queue>
#define ll long long
//#define zczc
using namespace std;
const int N=500010; 
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}

struct edge{
	int t,next;
}e[N*2];
int head[N],esum;
inline void add(int fr,int to){
	esum++;
	e[esum].t=to;
	e[esum].next=head[fr];
	head[fr]=esum;
}
int m,n,q,al,ar;
bool isa[N],isb[N];

struct node{
	int num,dis;
};
queue<node>qu;
bool vis[N];
void solve1(){
	int s1;
	for(int i=1;i<=n;i++){
		read(s1);isa[s1]=true;
		qu.push((node){s1,0});
		vis[s1]=true;
	}
	while(!qu.empty()){
		node now=qu.front();qu.pop();
		if(now.dis>=al&&now.dis<=ar){
			isb[now.num]=true;
		}
		if(now.dis>ar)break;
		for(int i=head[now.num];i;i=e[i].next){
			if(vis[e[i].t])continue;
			qu.push((node){e[i].t,now.dis+1});
			vis[e[i].t]=true;
		}
	}
	return;
}

int numa[N],numb[N];
ll fa1[N],fa2[N],fb[N];
void dfs1(int wh,int fa){
	numa[wh]=isa[wh],numb[wh]=isb[wh];
	for(int i=head[wh],th;i;i=e[i].next){
		th=e[i].t;
		if(th==fa)continue;
		dfs1(th,wh);
		numa[wh]+=numa[th];
		numb[wh]+=numb[th];
		fa1[wh]+=fa1[th]+(ll)numa[th];
		fa2[wh]+=fa2[th]+2*fa1[th]+(ll)numa[th];
		fb[wh]+=fb[th]+(ll)numb[th];
	}
}
void dfs2(int wh,int fa){
	if(fa){
		int numafa=numa[1]-numa[wh];
		int numbfa=numb[1]-numb[wh];
		ll fbfa=fb[fa]-(fb[wh]+(ll)numb[wh]);
		ll fa1fa=fa1[fa]-(fa1[wh]+(ll)numa[wh]);
		ll fa2fa=fa2[fa]-(fa2[wh]+2*fa1[wh]+(ll)numa[wh]);
		fb[wh]+=fbfa+numbfa;
		fa1[wh]+=fa1fa+(ll)numafa;
		fa2[wh]+=fa2fa+2*fa1fa+(ll)numafa;
	}
	for(int i=head[wh];i;i=e[i].next){
		if(e[i].t==fa)continue;
		dfs2(e[i].t,wh);
	}
	return;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int s1,s2;
	read(m);read(n);read(q);read(al);read(ar);
	for(int i=1;i<m;i++){
		read(s1);read(s2);
		add(s1,s2);add(s2,s1);
	}
	solve1(); 
	dfs1(1,0);
	dfs2(1,0);
    while(q--){
    	read(s1);
    	printf("%lld\n",fa2[s1]+fb[s1]);
	}
	
	return 0;
}

标签:半仙,int,ll,超能力,wh,fa,numa,妹子
来源: https://www.cnblogs.com/dai-se-can-tian/p/15196640.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有