51nod1462 树据结构

Problem

51nod

Solution

第一想法是链剖+线段树好像可以做,只不过细节有点多。不过好像还可以cdq分治,但是cdq写起来还是不爽,得树剖+线段树,时间复杂度也并不优秀 O ( m log ⁡ m log ⁡ n 2 ) O(m\log m\log n^2) O(mlogmlogn2)。我们考虑用线段树合并的方法来实现。

由于每次操作都是从某点加到根,那么这个操作用线段树合并很好搞。考虑把线段树的的区间搞成时间,那么对于2操作,我们其实就只需要统计一下之前的1操作的影响即可加入答案,这个在时间线段树上很好做,即每次向右子树走时加入左子树的贡献。当我们合并子树的线段树时,考虑一下子树之间会做什么贡献,有点类似POI的rot,而这个是差分的贡献,所以还要把儿子的答案累加一下。

时间复杂度 O ( m log ⁡ n ) O(m\log n) O(mlogn)

Code

#include <algorithm>
#include <cstdio>
#define rg register
#define pushup(rt) a[rt]=a[lc[rt]]+a[rc[rt]],b[rt]=b[lc[rt]]+b[rc[rt]]
using namespace std;
typedef long long ll;
const int maxn=100010,maxm=2000010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{int v,nxt;}edge[maxn];
int n,m,p,sz,head[maxn],rt[maxn],lc[maxm],rc[maxm];
ll res,ans[maxn],a[maxm],b[maxm];
inline void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}
void update(int l,int r,int pos,ll va,ll vb,int &rt)
{
	if(!rt) rt=++sz;
	if(l==r){a[rt]+=va;b[rt]+=vb;return ;}
	int m=(l+r)>>1;
	if(pos<=m) update(l,m,pos,va,vb,lc[rt]);
	else{res+=a[lc[rt]]*vb;update(m+1,r,pos,va,vb,rc[rt]);}
	pushup(rt);
}
void input()
{
	int op,x,d;
	read(n);
	for(rg int i=2;i<=n;i++){read(x);insert(x,i);}
	read(m);
	for(rg int i=1;i<=m;i++)
	{
		read(op);read(x);read(d);res=0;
		if(op==1) update(1,m,i,d,0,rt[x]);
		if(op==2) update(1,m,i,0,d,rt[x]);
		ans[x]+=res;
	}
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	res+=a[lc[x]]*b[rc[y]];
	res+=a[lc[y]]*b[rc[x]];
	lc[x]=merge(lc[x],lc[y]);
	rc[x]=merge(rc[x],rc[y]);
	pushup(x);
	return x;
}
void dfs(int x)
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		dfs(edge[i].v);res=0ll;
		rt[x]=merge(rt[x],rt[edge[i].v]);
		ans[x]+=res+ans[edge[i].v];
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	input();
	dfs(1);
	for(rg int i=1;i<=n;i++) printf("%lld\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值