uoj#58. 【WC2013】糖果公园

寒假里lbn大爷讲离线算法时就讲过了。。。然而现在才明白该怎么做。

这题就是树上带修莫队,带修莫队就是再加一维时间,然后分块size=n^(2/3),时间复杂度O(n^1.67)。树上莫队可以先求出括号序列,若x为y祖先,则两点间的路径为l[x]~l[y],否则就是r[x]~l[y]+lca。没了。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define N 100005
using namespace std;
int n,m,q,p,x,y,sz,v[N],w[N],first[N],c[N],len;
int dep[N],size[N],fa[N],Bson[N],top[N];
int ord[N*2],l[N],r[N],s,vis[N];
int cnt,Ti,la[N],Hz[N],pos[N*2];
ll now,ans[N];
struct edge{int to,next;}e[N*2];
struct Q{int x,y,z,id,lca;}Qry[N];
struct T{int x,u,v;}Mdy[N];
bool cmp(const Q &a,const Q &b)
{return pos[a.x]!=pos[b.x]?pos[a.x]<pos[b.x]:(pos[a.y]!=pos[b.y]?pos[a.y]<pos[b.y]:a.z<b.z);}
void link(int x,int y)
{
	e[++len]=(edge){y,first[x]};first[x]=len;
}
void dfs(int x)
{
	l[x]=++s;ord[s]=x;
	dep[x]=dep[fa[x]]+1;size[x]=1;
	for (int i=first[x],t;i;i=e[i].next)
		if ((t=e[i].to)!=fa[x])
		{
			fa[t]=x;dfs(t);size[x]+=size[t];
			if (size[t]>size[Bson[x]]) Bson[x]=t;
		}
	r[x]=++s;ord[s]=x;
}
void dfs(int x,int y)
{
	top[x]=y;
	if (Bson[x]) dfs(Bson[x],y);
	for (int i=first[x],t;i;i=e[i].next)
		if ((t=e[i].to)!=fa[x]&&t!=Bson[x])
			dfs(t,t);
}
int lca(int x,int y)
{
	for (;top[x]!=top[y];x=fa[top[x]])
		if (dep[top[x]]<dep[top[y]]) swap(x,y);
	return (dep[x]<dep[y])?x:y;
}
void add(int x)
{
	if (vis[x]) now-=(ll)v[c[x]]*w[Hz[c[x]]--];
	else now+=(ll)v[c[x]]*w[++Hz[c[x]]];
	vis[x]^=1;
}
void make(int x,int y)
{
	if (vis[x]) add(x),c[x]=y,add(x);else c[x]=y;
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for (int i=1;i<=m;i++)scanf("%d",&v[i]);
	for (int i=1;i<=n;i++)scanf("%d",&w[i]);
	for (int i=1;i<n;i++)scanf("%d%d",&x,&y),link(x,y),link(y,x);
	for (int i=1;i<=n;i++)scanf("%d",&c[i]),la[i]=c[i];
	dfs(1);dfs(1,1);sz=(int)pow(s,2.0/3);
	for (int i=1;i<=s;i++) pos[i]=(i-1)/sz;
	for (int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&p,&x,&y);
		if (p)
		{
			if (l[x]>l[y]) swap(x,y);
			int t=lca(x,y);
			if (x!=t) Qry[++cnt]=(Q){r[x],l[y],Ti,cnt,t};
			else Qry[++cnt]=(Q){l[x],l[y],Ti,cnt,0};
		}
		else Mdy[++Ti]=(T){x,la[x],y},la[x]=y;
	}
	sort(Qry+1,Qry+cnt+1,cmp);
	for (int x=1,y=0,z=0,i=1;i<=cnt;i++)
	{
		while(z<Qry[i].z) z++,make(Mdy[z].x,Mdy[z].v);
		while(z>Qry[i].z) make(Mdy[z].x,Mdy[z].u),z--;
		while(x>Qry[i].x) add(ord[--x]);
		while(y<Qry[i].y) add(ord[++y]);
		while(x<Qry[i].x) add(ord[x++]);
		while(y>Qry[i].y) add(ord[y--]);
		if (Qry[i].lca) add(Qry[i].lca);
		ans[Qry[i].id]=now;
		if (Qry[i].lca) add(Qry[i].lca);
	}
	for (int i=1;i<=cnt;i++)
		printf("%lld\n",ans[i]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值