BZOJ 3052: [wc2013]糖果公园 树上莫队

3052: [wc2013]糖果公园

Time Limit: 200 Sec  Memory Limit: 512 MB
Submit: 1274  Solved: 642
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

Sample Input

Sample Output

84
131
27
84

HINT



这就是传说中的树上莫队


先讲一讲树上莫队大法


算法流程:

树分块、询问排序、莫队大法


先树分块

我用的是“王室联邦分块法(我的块大小参考了hzwer)”(当然其他的也可以)详见BZOJ 1086: [SCOI2005]王室联邦 树分块

询问排序以路径起始点、终点所在的块为第一、二关键字,时间为第三关键字

之后就像带修改莫队一样搞

那么,如何实现树上路径的转移呢

这里粘一点东西 嘻嘻


用S(v, u)代表 v到u的路径上的结点的集合
用root来代表根结点,用lca(v, u)来代表v、u的最近公共祖先
那么S(v, u) = S(root, v) xor S(root, u) xor lca(v, u)
其中xor是集合的对称差
简单来说就是节点出现两次消掉
lca很讨厌,于是再定义
T(v, u) = S(root, v) xor S(root, u)
观察将curV移动到targetV前后T(curV, curU)变化
T(curV, curU) = S(root, curV) xor S(root, curU)
T(targetV, curU) = S(root, targetV) xor S(root, curU)
取对称差:
T(curV, curU) xor T(targetV, curU)= (S(root, curV) xor S(root, curU)) xor (S(root, targetV) xor S(root, curU))
由于对称差的交换律、结合律:
T(curV, curU) xor T(targetV, curU)= S(root, curV) xorS(root, targetV)
两边同时xor T(curV, curU):
T(targetV, curU)= T(curV, curU) xor S(root, curV) xor S(root, targetV)
发现最后两项很爽……哇哈哈
T(targetV, curU)= T(curV, curU) xor T(curV, targetV)
(有公式恐惧症的不要走啊 T_T)
也就是说,更新的时候,xor T(curV, targetV)就行了。
即,对curV到targetV路径(除开lca(curV, targetV))上的结点,将它们的存在性取反即可。

所以对于路径(u,v)向(x,y)转移时

(x,y)=(u,v) xor (u,x) xor (v,y)

(lca自己乱搞)


之后 就可以A掉这道题了


括弧

讲真。。lca从来没写对过(链剖版除外)

花了1个小时 只为了lca的1B


这三行是一生的痛。。。永远调不出。。。/悲号

for(i=20;i>=0;--i)if(fa[u][i]^fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];

AC代码

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
void print(ll x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=100100;

int n,m,block,qn,cn,bel[N],dfn[N],clr[N];

int ecnt,last[N];
struct EDGE{int to,nt;}e[N<<1];
inline void add(int u,int v)
{e[++ecnt]=(EDGE){v,last[u]};last[u]=ecnt;}

struct question{int u,v,time,id;}q[N];
inline bool cmp(const question &x,const question &y)
{
	if(bel[x.u]==bel[y.u]&&bel[x.v]==bel[y.v])return x.time<y.time;
	else if(bel[x.u]==bel[y.u])return bel[x.v]<bel[y.v];
	return bel[x.u]<bel[y.u];
}

struct change_color{int x,y,pre;}chg[N];

int prvn;

int cnt,size[N],d[N],fa[N][21],s[N],top;

void getblock(int u)
{
	register int i;
	dfn[u]=++cnt;s[++top]=u;
	for(i=last[u];i;i=e[i].nt)
	{
		if(e[i].to==fa[u][0])continue;
		fa[e[i].to][0]=u;d[e[i].to]=d[u]+1;getblock(e[i].to);
		size[u]+=size[e[i].to];
		if(size[u]>=block)
		{
			size[u]=0;++prvn;
			while(s[top]^u)bel[s[top--]]=prvn;
		}
	}
	size[u]++;
}

void fill(int u,int tp)
{
	bel[u]?tp=bel[u]:bel[u]=tp;register int i;
	for(i=last[u];i;i=e[i].nt)if(e[i].to^fa[u][0])fill(e[i].to,tp);
}

void init()
{register int i,j;for(j=1;j<=20;++j)for(i=1;i<=n;++i)fa[i][j]=fa[fa[i][j-1]][j-1];}

inline int getlca(int u,int v)
{
	if(d[u]<d[v])swap(u,v);
	register int len=d[u]-d[v],i;
	for(i=0;(1<<i)<=len;++i)if((1<<i)&len)u=fa[u][i];
	if(u==v)return u;
	for(i=20;i>=0;--i)if(fa[u][i]^fa[v][i])
	u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}

ll res,W[N],V[N];

bool vis[N];

int num[N];

inline void cal(int u)
{vis[u]?res-=W[num[clr[u]]--]*V[clr[u]]:res+=W[++num[clr[u]]]*V[clr[u]];vis[u]^=1;}

void work(int u,int v)
{while(u^v){if(d[u]<d[v])swap(u,v);cal(u);u=fa[u][0];}}

void modify(int x,int y)
{if(vis[x]){cal(x);clr[x]=y;cal(x);}clr[x]=y;}

ll ans[N];

void solve()
{
	register int i,j,lca;
	for(i=1;i<=q[1].time;++i)modify(chg[i].x,chg[i].y);
	lca=getlca(q[1].u,q[1].v);work(q[1].u,q[1].v);cal(lca);ans[q[1].id]=res;cal(lca);
	for(i=2;i<=qn;++i)
	{
		for(j=q[i-1].time+1;j<=q[i].time;++j)modify(chg[j].x,chg[j].y);
		for(j=q[i-1].time;j>q[i].time;--j)modify(chg[j].x,chg[j].pre);
		work(q[i-1].u,q[i].u);
		work(q[i-1].v,q[i].v);
		lca=getlca(q[i].u,q[i].v);
		cal(lca);ans[q[i].id]=res;cal(lca);
	}
}

int pre_clr[N];

int main()
{
	n=read();m=read();
	register int i,opt,u,v,Q=read();
	for(i=1;i<=m;++i)V[i]=read();
	for(i=1;i<=n;++i)W[i]=read();
	block=pow(n,2.0/3)*0.5;
	for(i=1;i<n;++i){u=read();v=read();add(u,v);add(v,u);}
	for(i=1;i<=n;++i)pre_clr[i]=clr[i]=read();
	getblock(1);fill(1,prvn);init();
	for(i=1;i<=Q;++i)
	{
		opt=read();u=read();v=read();
		if(opt){if(dfn[u]>dfn[v])swap(u,v);q[++qn].time=cn;q[qn].u=u;q[qn].v=v;q[qn].id=qn;}
		else{chg[++cn].x=u;chg[cn].y=v;chg[cn].pre=pre_clr[u];pre_clr[u]=v;}
	}
	sort(q+1,q+1+qn,cmp);
	solve();
	for(i=1;i<=qn;++i)print(ans[i]),puts("");
	return 0;
}
/*
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2

84
131
27
84
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值