【BZOJ3589】动态树 树链剖分+线段树

Description

别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件

事件0:

这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.

事件1:

小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.

Input

第一行一个整数n(1<=n<=200,000), 即节点数.

接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.

在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.

最后nQ行, 每行开头要么是0, 要么是1.

如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.

如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.

Output

对于每个事件1, 输出询问的果子数.

Sample Input

5
1 2
2 3
2 4
1 5
3
0 1 1
0 2 3
1 2 3 1 1 4

Sample Output

13

HINT

1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.

生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.

Sol

这题为啥要容斥啊,不就一线段树+树剖题吗。(不过BIT的log*32确实比树剖的俩log*5快)

我们在线段树中维护当前的总和sum以及实际有效的值val,每次查询的时候,把树链上面的点系数改成1,之后获得答案,然后再把整棵树的系数改成0。

写完之后居然没调就过了样例???(逃

Code

#include <bits/stdc++.h>
#define mid ((s[x].l+s[x].r)>>1)
using namespace std;
struct seg{int l,r,sum,val,tag,lzy;}s[2000005];
int n,x,y,op,m,z,I,fa[400005],dep[400005],top[400005],son[400005],siz[400005],id[400005];
vector<int>e[400005];
int dfs1(int x)
{
	int maxx=0,tot=0;
	for(int i=0;i<e[x].size();i++) if(e[x][i]!=fa[x])
	{
		dep[e[x][i]]=dep[x]+1;fa[e[x][i]]=x;tot+=dfs1(e[x][i]);
		if(siz[e[x][i]]>maxx) maxx=siz[e[x][i]],son[x]=e[x][i];
	}
	return siz[x]=tot+1;
}
void dfs2(int x,int Fa)
{
	id[x]=++I;top[x]=Fa;
	if(son[x]) dfs2(son[x],Fa);
	for(int i=0;i<e[x].size();i++) if(e[x][i]!=fa[x]&&e[x][i]!=son[x]) dfs2(e[x][i],e[x][i]);
}
void build(int x,int L,int R)
{
	s[x]=(seg){L,R,0,0,-1,0};
	if(L==R) return;
	build(x*2,L,mid);build(x*2+1,mid+1,R);
}
void down(int x)
{
	s[x*2].sum+=s[x].lzy*(s[x*2].r-s[x*2].l+1);s[x*2+1].sum+=s[x].lzy*(s[x*2+1].r-s[x*2+1].l+1);
	s[x*2].lzy+=s[x].lzy;s[x*2+1].lzy+=s[x].lzy;s[x].lzy=0;
	if(s[x].tag==-1) return;
	s[x*2].val=s[x].tag*s[x*2].sum;s[x*2+1].val=s[x].tag*s[x*2+1].sum;
	s[x*2].tag=s[x*2+1].tag=s[x].tag;s[x].tag=-1;
}
void update(int x,int L,int R,int V,int O)
{
	down(x);
	if(L<=s[x].l&&s[x].r<=R)
	{
		if(!O) s[x].val=s[x].sum*V,s[x].tag=V;
		else s[x].lzy+=V,s[x].sum+=V*(s[x].r-s[x].l+1);
		return; 
	}
	if(L<=mid) update(x*2,L,R,V,O);if(R>mid) update(x*2+1,L,R,V,O);
	s[x].sum=s[x*2].sum+s[x*2+1].sum;s[x].val=s[x*2].val+s[x*2+1].val;
}
void solve(int x,int y)
{
	for(;top[x]!=top[y];update(1,id[top[x]],id[x],1,0),x=fa[top[x]]) if(dep[top[x]]<dep[top[y]]) swap(x,y);
	if(dep[x]>dep[y]) swap(x,y);update(1,id[x],id[y],1,0);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++) scanf("%d%d",&x,&y),e[x].push_back(y),e[y].push_back(x);
	dfs1(1);dfs2(1,1);build(1,1,n);
	for(scanf("%d",&m);m--;)
	{
		scanf("%d",&op);
		if(!op){scanf("%d%d",&x,&y),update(1,id[x],id[x]+siz[x]-1,y,1);continue;}
		for(scanf("%d",&z);z--;) scanf("%d%d",&x,&y),solve(x,y);
		printf("%d\n",s[1].val&0x7fffffff);update(1,1,n,0,0);
	}
}
posted @ 2018-08-03 11:25  CK6100LGEV2  阅读(172)  评论(0编辑  收藏  举报