BZOJ 3589 动态树 树链剖分+容斥原理

题目大意:给定一棵以1为根的有根树,每个节点有点权,提供两种操作:

1.以某个节点为根的子树所有节点权值+x

2.求一些链的并集的点权和,其中这些链都是由某个节点出发指向根

首先子树修改,链上查询,树链剖分的WT~

然后这些链上的每个点的点权都只能被加一次,肯定不能打标记,由于k<=5,我们考虑容斥原理

总权值=单链-两两之交+三链之交……

状压枚举即可 两条链的交集求法如下:

1.求两条链底的LCA

2.若LCA的深度小于其中一条链的链顶深度 交集为空 返回0

3.返回一条链 链底为LCA 链顶为两条链顶中深度较大的那个

此题mod2^31,直接自然溢出int,然后答案对2147483647取与即可 出题人真贴心-。-

时间复杂度O( m * log^2n * 2^k ) 看到这复杂度我都尿了0.0 不TLE真是太好了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 200200
#define ls tree[p].lson
#define rs tree[p].rson
using namespace std;
struct abcd{
	int to,next;
}table[M<<1];
struct Tree{
	int lson,rson;
	int num,mark;
}tree[M<<1];int tree_tot;
int head[M],tot;
int n,m,k;
int fa[M],son[M],dpt[M],siz[M],top[M],pos[M],cnt;
int digit[M];
pair<int,int>br[10];
void Build_Tree(int p,int x,int y)
{
	int mid=x+y>>1;
	if(x==y)
		return ;
	ls=++tree_tot;rs=++tree_tot;
	Build_Tree(ls,x,mid);
	Build_Tree(rs,mid+1,y);
}
void Modify(int p,int x,int y,int l,int r,int v)
{
	int mid=x+y>>1;
	if(x==l&&y==r)
	{
		tree[p].num+=(y-x+1)*v;
		tree[p].mark+=v;
		return ;
	}
	if(tree[p].mark)
	{
		tree[ls].mark+=tree[p].mark;
		tree[rs].mark+=tree[p].mark;
		tree[ls].num+=tree[p].mark*(mid-x+1);
		tree[rs].num+=tree[p].mark*(y-mid);
		tree[p].mark=0;
	}
	if(r<=mid)
		Modify(ls,x,mid,l,r,v);
	else if(l>mid)
		Modify(rs,mid+1,y,l,r,v);
	else Modify(ls,x,mid,l,mid,v) , Modify(rs,mid+1,y,mid+1,r,v);
	tree[p].num=tree[ls].num+tree[rs].num;
}
int Get_Ans(int p,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(x==l&&y==r)
		return tree[p].num;
	if(tree[p].mark)
	{
		tree[ls].mark+=tree[p].mark;
		tree[rs].mark+=tree[p].mark;
		tree[ls].num+=tree[p].mark*(mid-x+1);
		tree[rs].num+=tree[p].mark*(y-mid);
		tree[p].mark=0;
	}
	if(r<=mid) return Get_Ans(ls,x,mid,l,r);
	if(l>mid) return Get_Ans(rs,mid+1,y,l,r);
	return Get_Ans(ls,x,mid,l,mid) + Get_Ans(rs,mid+1,y,mid+1,r);
}
void DFS1(int x)
{
	int i;
	siz[x]=1;
	dpt[x]=dpt[fa[x]]+1;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x])
			continue;
		fa[table[i].to]=x;
		DFS1(table[i].to);
		siz[x]+=siz[table[i].to];
		if(siz[table[i].to]>siz[son[x]])
			son[x]=table[i].to,swap(table[head[x]].to,table[i].to);
	}
}
void DFS2(int x)
{
	int i;
	pos[x]=++cnt;
	top[x]=son[fa[x]]==x?top[fa[x]]:x;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x])
			continue;
		DFS2(table[i].to);
	}
}
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
int Query(int x,int y)
{
	int re=0,fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dpt[fx]<dpt[fy])
			swap(x,y),swap(fx,fy);
		re+=Get_Ans(0,1,n,pos[fx],pos[x]);
		fx=top[x=fa[fx]];
	}
	if(dpt[x]<dpt[y])
		swap(x,y);
	re+=Get_Ans(0,1,n,pos[y],pos[x]);
	return re;
}
int LCA(int x,int y)
{
	int fx=top[x],fy=top[y];
	while(fx!=fy)
	{
		if(dpt[fx]<dpt[fy])
			fy=top[y=fa[fy]];
		else
			fx=top[x=fa[fx]];
	}
	return dpt[x]<dpt[y]?x:y;
}
pair<int,int> Cross(pair<int,int> x,pair<int,int> y)
{
	int lca=LCA(x.first,y.first);
	if(dpt[lca]<dpt[x.second]||dpt[lca]<dpt[y.second])
		return make_pair(-1,-1);
	return make_pair(lca,dpt[x.second]>dpt[y.second]?x.second:y.second);
}
int Calculate(int x)
{
	int i;
	pair<int,int>re=make_pair(0,0);
	for(i=1;x;i++,x>>=1)
		if(x&1)
		{
			if(re.first==0)
				re=br[i];
			else
				re=Cross(re,br[i]);
			if(re.first==-1)
				return 0;
		}
	return Query(re.first,re.second);
}
int Inclusion_Exclusion_Principle()
{
	int i,re=0;
	for(i=1;i<1<<k;i++)
		re+=Calculate(i)*(digit[i]&1?1:-1);
	return re;
}
int main()
{

	//freopen("3589.in","r",stdin);
	//freopen("3589.out","w",stdout);

	int i,j,x,y,p;
	cin>>n;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		Add(x,y);
		Add(y,x);
	}
	DFS1(1);
	DFS2(1);
	Build_Tree(0,1,n);
	for(i=1;i<1<<5;i++)
		digit[i]=digit[i>>1]+(i&1);
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d",&p);
		if(!p)
		{
			scanf("%d%d",&x,&y);
			Modify(0,1,n,pos[x],pos[x]+siz[x]-1,y);
		}
		else
		{
			scanf("%d",&k);
			for(j=1;j<=k;j++)
			{
				scanf("%d%d",&x,&y);
				if(dpt[x]<dpt[y])
					swap(x,y);
				br[j]=pair<int,int>(x,y);
			}
			printf("%d\n", Inclusion_Exclusion_Principle()&2147483647 );
		}
	}
}
时间: 2024-08-30 06:49:51

BZOJ 3589 动态树 树链剖分+容斥原理的相关文章

BZOJ 3589 动态树 树链剖分+容斥定理

题目大意:给出一棵树,每一个节点有一个权值,一开始所有节点的权值都是0.有两种操作,0 x y代表以x为根节点的子树上所有点的权值增加y.1 k a1 b1 a2 b2 --ak bk代表询问.一共有k条边( k <= 5),这些边保证是一个点到根节点的路径上的一段.问这些路径段上的点的权值和是多少,可能有多条路径重叠的情况. 思路:子树修改,区间查询,很明显用树链剖分解决,树链剖分维护一个size域,那么x的子树的范围就是pos[x]到pos[x] + size[x] - 1这一段上,可以用线

BZOJ 3589 动态树 树链拆分+纳入和排除定理

标题效果:鉴于一棵树.每个节点有一个右值,所有节点正确启动值他们是0.有两种操作模式,0 x y代表x右所有点的子树的根值添加y. 1 k a1 b1 a2 b2 --ak bk代表质疑. 共同拥有者k边缘( k <= 5),这些边保证是一个点到根节点的路径上的一段. 问这些路径段上的点的权值和是多少,可能有多条路径重叠的情况. 思路:子树改动,区间查询,非常明显用树链剖分解决,树链剖分维护一个size域.那么x的子树的范围就是pos[x]到pos[x] + size[x] - 1这一段上.能够

bzoj 3589: 动态树【树链剖分+容斥】

因为一开始调试不知道unsigned怎么输出就没有加\n结果WA了一上午!!!!!然而最后放弃了unsigned选择了&2147483647 首先链剖,因为它所给的链一定是某个点到根的路径上的一段(一开始没看到),也就是说链是不会拐弯的,那么考虑容斥,加上每条链的长度减去两条链的交的长度加上三条链的交的长度... 关于求链的交,因为链不会拐弯,所以对于两条链上深度较深的两个点\( (v_1,v_2) \)求\( lca \),如果\( lca \)的深度小于两条链的较浅点的任意一个,那么这两条链

CF487E Tourists 圆方树、树链剖分

传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就是一个反例 上面这个图如果缩点双会缩成\(3\)个,但是缩边双会将整个图缩成\(1\)个点. 假如我们询问的是\((1,4)\)之间的简单路径,而图中权值最小的点为\(7\)号点,那么如果缩成了边双联通分量,你的答案会是\(7\)号点的权值,意即认为可以走到\(7\)号点,但实际上如果到\(7\)号

【GDOI2020模拟01.16】树上的鼠 (博弈+长链剖分优化dp)

https://gmoj.net/senior/#contest/show/2989/2 思考什么时候先手会赢. 一开始双方都不会希望走到直径的端点上,因为那样对方就可以走直径而使自己输掉. 删掉直径的端点,考虑剩下的树的子问题. 如果又走到端点去了,对面就走到另外一个端点,那我就走到下一层的直径端点去了. 所以大家一直都不想走到直径端点. 一直删,如果最后只剩1一个点,说明先手必败,否则先手必胜. 如果是一条链,就是链的两边的长度不等先手就必胜. 如果是一棵树,考虑随便找一条直径,每次删去它的

【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)

3589: 动态树 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 405  Solved: 137[Submit][Status][Discuss] Description 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝

动态树之树链剖分

一些不需要link-cut操作的树上路径的题可以用树链剖分做,常数比lct小多了. 学习了下hld(树链剖分),嗯,挺简单的.hld可以在树中的操作有很多,hld可以说只是一种概念结构,它可以套很多其它的数据结构来进行操作,比如我现在只要求路径最值和求和,那么套线段树就行了:如果我要求第k大,可以套splay和主席树(这个不知道),也可以套分块(不会,分块以后学,必须学..)但是我觉得,树剖比lct还要难写..我lct一下就能写出来了..可是lct的常数,不忍直视..概念: 重儿子:num[u]

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

BZOJ 1969: [Ahoi2005]LANE 航线规划( 树链剖分 )

首先我们要时光倒流, 倒着做, 变成加边操作维护关键边. 先随意搞出一颗树, 树上每条边都是关键边(因为是树, 去掉就不连通了)....然后加边(u, v)时, 路径(u, v)上的所有边都变成非关键边了, 因为形成了环, 环上任意2点有2条路径...下图, 加上蓝色的边, 红色x的边就变成了非关键边. 所以树链剖分维护一下...时间复杂度O(NlogN+MlogM+Qlog^2N), 可以AC. 翻了翻ZY的标解:“动态维护树+最近公共祖先查询”....复杂度是O(NlogN+M+QlogN)