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

13 篇文章 0 订阅
13 篇文章 1 订阅

题目大意:给定一棵以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 );
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值