BZOJ-3052. [wc2013]糖果公园

这篇博客详细介绍了如何将树上带修改莫队和树上莫队算法结合,以解决一类具有修改操作的树形问题。作者通过实例展示了如何实现这种结合,并给出了完整的C++代码,包括数据结构定义、树的DFS遍历、LCA计算等关键步骤。同时,博客探讨了最优块大小的选择和排序策略,以提高算法效率。
摘要由CSDN通过智能技术生成

读题很容易知道这就是一道简单的树上带修改莫队,树上待修改莫队只要将待修改莫队和树上莫队结合起来使用就可以了。(感谢darkbzoj)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<ctime>
#include<algorithm>
#include<sstream>
#include<bitset>
#define scand(a) scanf("%d",&a)
#define scandd(a,b) scanf("%d%d",&a,&b)
#define scanddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define mst(a,b) memset(a,b,sizeof(a))
#define lowbit(x) (x&-x)
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3fLL;
const int maxn=2e5+5;
const ll mod=1e9+7;
int n,m,blo;
int a[maxn],v[maxn],w[maxn];
struct nod1
{
	int l,r,lca,id,pre;
}p[maxn];
struct nod
{
	int nxt,to;
}edge[maxn<<1];
struct nod2
{
	int pos,val;
}que[maxn];
int head[maxn],cnt=1;
void add(int u,int v)
{
	edge[++cnt].nxt=head[u];
	edge[cnt].to=v;
	head[u]=cnt;
}
int loc[maxn],st[maxn],ed[maxn],ti;
int fa[maxn][21];
int dep[maxn];
void dfs(int u,int f,int depp)
{
	loc[++ti]=u;
	st[u]=ti;
	dep[u]=depp;
	fa[u][0]=f;
	for(int i=1;i<=20;i++)
	{
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==f)continue;
		dfs(v,u,depp+1);
	}
	loc[++ti]=u;
	ed[u]=ti;
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)
	{
		if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
	}
	if(x==y)return x;
	for(int i=20;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
	}
	return fa[x][0];
}
bool cmp(nod1 a,nod1 b)
{
	if(a.l/blo==b.l/blo)
	{
		if(a.r/blo==b.r/blo)return a.pre<b.pre;
		return a.r<b.r;
	}
	return a.l<b.l;
}
ll ans[maxn];
ll k=0;
int vis[maxn];
int sum[maxn];
void upd(int x)
{
	
	if(!vis[x])
	{
		sum[a[x]]++;
		k+=1LL*w[sum[a[x]]]*v[a[x]];
	}
	else
	{
		k-=1LL*w[sum[a[x]]]*v[a[x]];
		sum[a[x]]--;
		
	}
	vis[x]^=1;
}
void cupd(int now,int i)
{
	int x=que[now].pos;
	if(vis[x])
	{
	/*
	这里的话,由于存在着LCA的缘故,所以直接判断该点有没有访问过就行了,如果访问过,显然这次修改是对答案产生影响的。
	*/
		upd(x);
		swap(a[x],que[now].val);
		upd(x);
	}
	else swap(a[x],que[now].val);//无论有没有影响都得swap,和带修莫队一样
}
int main()
{
    #ifdef local
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif
    int n,m,q;
    scanddd(n,m,q);
    for(int i=1;i<=m;i++)scand(v[i]);
    for(int i=1;i<=n;i++)scand(w[i]);
    for(int i=1;i<n;i++)
    {
    	int u,v;
    	scandd(u,v);
    	add(u,v);
    	add(v,u);
	}
	for(int i=1;i<=n;i++)scand(a[i]);
	dfs(1,0,1);
	blo=pow(ti,0.6666);//由于排序是按照带修莫队排的所以最优的块大小肯定是ti^(2/3)
	int lenp=0,lenq=0;
	for(int i=1;i<=q;i++)//下面按照带修莫队+树上莫队结合着写就行
	{
		int type,x,y;
		scanddd(type,x,y);
		if(!type)
		{
			lenq++;
			que[lenq].pos=x;
			que[lenq].val=y;
		}
		else
		{
			lenp++;
			int z=lca(x,y);
			if(st[x]>st[y])swap(x,y);
			p[lenp].pre=lenq;
			p[lenp].id=lenp;
			if(z==x)
			{
				p[lenp].l=st[x];
				p[lenp].r=st[y];
			}
			else
			{
				p[lenp].lca=z;
				p[lenp].l=ed[x];
				p[lenp].r=st[y];
			}
		}
	}
	sort(p+1,p+1+lenp,cmp);
	int l=1,r=0,now=0;
	for(int i=1;i<=lenp;i++)
	{
		while(r>p[i].r)upd(loc[r--]);
		while(r<p[i].r)upd(loc[++r]);
		while(l<p[i].l)upd(loc[l++]);
		while(l>p[i].l)upd(loc[--l]);
		while(now<p[i].pre)cupd(++now,i);
		while(now>p[i].pre)cupd(now--,i);
		if(p[i].lca)upd(p[i].lca);
		ans[p[i].id]=k;
		if(p[i].lca)upd(p[i].lca);
	}
	for(int i=1;i<=lenp;i++)printf("%lld\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值