【BZOJ 3052】 [wc2013]糖果公园

3052: [wc2013]糖果公园

Time Limit: 250 Sec   Memory Limit: 512 MB
Submit: 481   Solved: 176
[ Submit][ Status]

Description

Input

Output

Sample Input

Sample Input

Sample Output

84
131
27
84

HINT



带修改的树上莫队。


完成 【BZOJ 3757】(不带修改的树上莫队)之后,这道题就好理解了。


与bzoj3757一样,先对树分块,把询问以块和查询时间为关键字排序,关键就是转移了。


 (curV,curU)到(targetV,targetU)的转移与bzoj3757一致,那么修改怎么处理呢?


预处理出每一个询问操作的前一个修改操作是第几个。


分两种情况:

1.输入中cur比target靠前,那么我们只要把从cur后面到target之间的所有修改处理一次就可以。


2.输入中cur比target靠后,那么我们需要预处理出每一个修改操作中修改之前的颜色是pre,把cur到target之间的修改处

理一次,与上面不同的是现在要把当前颜色修改成pre。


综上,只要在(curV,curU)到(targetV,targetU)转移之前对时间进行上述转移即可。


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#define M 100005
#define LL long long
using namespace std;
LL v[M],w[M],Ans[M],ans=0LL;
int k,QQ1,dfs_clock=0,dep[M],dfn[M],be[M],b[25],f[M][22],h[M],tot=0,cnt=0,Q,QQ,n,m,vi[M],s[M],top=0,c[M],num[M],pre[M];
struct edge
{
	int y,ne;
}e[M*2];
struct Q1
{
	int x,y,k,pre;
}q1[M],qq[M];
struct Query
{
	int x,y,id,t;
}q[M];
void r(int &tmp)
{
	tmp=0;
	char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar());
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=(tmp*10)+ch-'0';
}
void R(LL &tmp)
{
	tmp=0LL;
	char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar());
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=(tmp*10)+ch-'0';
}
void Addedge(int x,int y)
{
	e[++tot].y=y;
	e[tot].ne=h[x];
	h[x]=tot;
}
void dfs(int x)
{
	dfn[x]=++dfs_clock;
	int bottom=top;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (f[x][0]==y) continue;
		f[y][0]=x;
		dep[y]=dep[x]+1;
		dfs(y);
		if (top-bottom>=k)
		{
			cnt++;
			while (top!=bottom)
				be[s[top--]]=cnt;
		}
	}
	s[++top]=x;
}
void prepare()
{
	b[0]=1;
	for (int i=1;i<=16;i++)
		b[i]=b[i-1]<<1;
	for (int j=1;j<=16;j++)
		for (int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
}
bool cmp(Query a,Query b)
{
	if (be[a.x]==be[b.x]) 
	{
		if (be[a.y]==be[b.y])
			return a.t<b.t;
		return be[a.y]<be[b.y];
	}
	return be[a.x]<be[b.x];
}
void Calc(int x)
{
	if (vi[x])
		ans=ans-(v[c[x]]*(w[num[c[x]]--]));
	else
		ans=ans+(v[c[x]]*(w[++num[c[x]]]));
	vi[x]^=1;
}
void Modifytime(int x,int y)
{
	if (q[x].id<q[y].id)
	{
		for (int i=q[x].id+1;i<=q[y].id;i++)
			{
				int now=c[qq[i].x],mo=qq[i].y;
				if (vi[qq[i].x])
				{
					Calc(qq[i].x);
					c[qq[i].x]=mo;
					Calc(qq[i].x);
				}
				else c[qq[i].x]=mo;
			}
		return;
	}
	for (int i=q[x].id;i>q[y].id;i--)
			{
				int now=c[qq[i].x],mo=qq[i].pre;
				if (vi[qq[i].x])
				{
					Calc(qq[i].x);
					c[qq[i].x]=mo;
					Calc(qq[i].x);
				}
				else c[qq[i].x]=mo;
			}
}
void Move(int &x,int deep)
{
	for (int i=16;i>=0;i--)
		if (dep[f[x][i]]>=deep)
			x=f[x][i];
}
int Getlca(int x,int y)
{
	if (dep[x]>dep[y]) swap(x,y);
	Move(y,dep[x]);
	if (x==y) return x;
	for (int i=16;i>=0;i--)
		if (f[x][i]!=f[y][i]) 
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
void Solve(int x,int y)
{
	while (x!=y)
	{
		if (dep[x]<dep[y])  swap(x,y);
		Calc(x);
		x=f[x][0];
	}
}
int main()
{
        r(n),r(m),r(QQ);
        k=sqrt(n);
	for (int i=1;i<=m;i++)
		R(v[i]);
	for (int i=1;i<=n;i++)
		R(w[i]);
	for (int i=1;i<n;i++)
	{
		int x,y;
		r(x),r(y);
		Addedge(x,y),Addedge(y,x);
	}
	dep[1]=1;
	dfs(1);
	while (top)
		be[s[top--]]=cnt;
	prepare();
	for (int i=1;i<=n;i++)
		r(c[i]),pre[i]=c[i];
	for (int i=1;i<=QQ;i++)
	{
		int t,x,y;
		r(t),r(x),r(y);
		if (!t)
		{
			QQ1++;
			qq[QQ1].x=x,qq[QQ1].y=y;
			qq[QQ1].pre=pre[x];
			pre[x]=y;
		}
		else
		{
			Q++;
			if (dfn[x]>dfn[y])
			 swap(x,y);
			q[Q].x=x,q[Q].y=y;
			q[Q].id=QQ1;
			q[Q].t=Q;
		}
	}
	sort(q+1,q+1+Q,cmp);
	ans=0LL;
	q[0].id=0,q[0].x=q[0].y=1;
	for (int i=1;i<=Q;i++)
	{
		Modifytime(i-1,i);
		Solve(q[i-1].x,q[i].x);
		Solve(q[i-1].y,q[i].y);
		int a=Getlca(q[i].x,q[i].y);
		Calc(a);
		Ans[q[i].t]=ans;
		Calc(a);
	}
	for (int i=1;i<=Q;i++)
		printf("%lld\n",Ans[i]);
	return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值