Written with StackEdit.

Description

\(Candyland\) 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩。
糖果公园的结构十分奇特,它由 \(n\) 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 \(1\)\(n\)。有 \(n – 1\) 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。

糖果公园所发放的糖果种类非常丰富,总共有 \(m\) 种,它们的编号依次为 \(1\)\(m\)。每一个糖果发放处都只发放某种特定的糖果,我们用 \(C_i\)​ 来表示 \(i\) 号游览点的糖果。

来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。

大家对不同类型糖果的喜爱程度都不尽相同。 根据游客们的反馈打分,我们得到了糖果的美味指数, 第 \(i\) 种糖果的美味指数为 \(V_i\)​。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 \(i\) 次品尝某类糖果的新奇指数 \(W_i\)​。如果一位游客第 \(i\) 次品尝第 \(j\) 种糖果,那么他的愉悦指数 \(H\)将会增加对应的美味指数与新奇指数的乘积,即 \(V_j​×W_i\)​。这位游客游览公园的愉悦指数最终将是这些乘积的和。

当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 \(m\) 种中的一种),这样的目的是能够让游客们总是感受到惊喜。

糖果公园的工作人员小 \(A\) 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 \(A\) 一看到密密麻麻的数字就觉得头晕,作为小 \(A\) 最好的朋友,你决定帮他一把。

Input

第一行包含三个正整数 \(n\), \(m\), \(q\), 分别表示游览点个数、 糖果种类数和操作次数。

第二行包含 \(m\) 个正整数 \(V_1, V_2​, …, V_m\)​。

第三行包含 \(n\) 个正整数 \(W_1​, W_2​, …, W_n\)​。

第四行到第 \(n + 2\) 行,每行包含两个正整数 \(A_i\)​, \(B_i\)​,表示这两个游览点之间有路径可以直接到达。

\(n + 3\) 行包含 \(n\) 个正整数 \(C_1, C_2​, …, C_n\)

接下来 \(q\) 行, 每行包含三个整数 \(Type, x, y\),表示一次操作:

\(Type\)\(0\),则 \(1 ≤ x ≤ n\)\(1 ≤ y ≤ m\),表示将编号为 \(x\) 的游览点发放的糖果类型改为 \(y\)

\(Type\)\(1\),则 \(1 ≤ x,y ≤ n\),表示对出发点为 \(x\),终止点为 \(y\) 的路线询问愉悦指数。

Output

按照输入的先后顺序,对于每个 \(Type\)\(1\) 的操作输出一行,用一个正整数表示答案。

Solution

  • 树上带修莫队板子题.
  • 树上分块使用王室联邦的分块,能保证每块大小在\([B,3B]\)内.
  • 移动时,若从\((pu,pv)\)移动到\((u,v)\),则只需对路径\((pu,u),(pv,v)\)的点是否包含情况取反,\(LCA\)不处理,保存答案前对\((u,v)\)\(LCA\)取反,答案保存后再将这个点取反回去.
  • 正确性?我也不会证.大概可以参照VFleaKingdalao的blog.
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline LoveLive read()
{
	LoveLive out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		{
			fh=-1;
			jp=getchar();
		}
	while (jp>='0'&&jp<='9')
		{
			out=out*10+jp-'0';
			jp=getchar();
		}
	return out*fh;
}
const int MAXT=17;
const int MAXN=1e5+10;
int ecnt=0,head[MAXN];
int nx[MAXN<<1],to[MAXN<<1];
inline void addedge(int u,int v)
{
	++ecnt;
	nx[ecnt]=head[u];
	to[ecnt]=v;
	head[u]=ecnt;
}
inline void ins(int u,int v)
{
	addedge(u,v);
	addedge(v,u);
}
int n,m,Q;
int cntQ=0,cntC=0;
LoveLive Ans[MAXN];
int belong[MAXN],cntB=0;
struct Query{
	int u,v;
	int id,tim;
	bool operator < (const Query &rhs) const 
		{
			if(belong[u]!=belong[rhs.u])
				return belong[u]<belong[rhs.u];
			if(belong[v]!=belong[rhs.v])
				return belong[v]<belong[rhs.v];
			return tim<rhs.tim;
		}
}q[MAXN];
int stk[MAXN],top=0,BlockSize;
int fa[MAXN],dep[MAXN];
void dfs(int u,int Fa)
{
	fa[u]=Fa;
	dep[u]=dep[Fa]+1;
	int bot=top;
	for(int i=head[u];i;i=nx[i])
		{
			int v=to[i];
			if(v!=Fa)
				{
					dfs(v,u);
					if(top-bot>=BlockSize)
						{
							++cntB;
							for(;top!=bot;--top)
								belong[stk[top]]=cntB;
						}
				}
		}
	stk[++top]=u;
}
void BuildBlocks()
{
	dfs(1,0);
	for(;top;--top)
		belong[stk[top]]=cntB;
}
int up[MAXN][MAXT+1];
void init_lca()
{
	for(int i=1;i<=n;++i)
		up[i][0]=fa[i];
	for(int j=1;j<=MAXT;++j)
		for(int i=1;i<=n;++i)
			up[i][j]=up[up[i][j-1]][j-1];
}
int query_lca(int a,int b)
{
	if(a==b)
		return a;
	if(dep[a]>dep[b])swap(a,b);
	int dl=dep[b]-dep[a];
	for(int i=0;i<=MAXT && (1<<i)<=dl;i++)
		if(dl&(1<<i))
			b=up[b][i];
	if(a==b)return a;
	for(int i=MAXT;i>=0;--i)
		{
			if(up[a][i]!=up[b][i])
				{
					a=up[a][i];
					b=up[b][i];
				}
		}
	return up[b][0];
}
int newx[MAXN],pos[MAXN],tim[MAXN];//存储修改操作的信息
LoveLive val[MAXN],w[MAXN];
int col[MAXN];
LoveLive res;
int pu,pv,cnt[MAXN];//记录当前的路径和答案
int cur;//当前时间 
int vis[MAXN];
void reverse(int u)//将u在路径上的情况取反 
{
	if(vis[u])
		{
			res-=val[col[u]]*w[cnt[col[u]]];
			--cnt[col[u]];
		}
	else
		{
			++cnt[col[u]];
			res+=val[col[u]]*w[cnt[col[u]]];
		}
	vis[u]^=1;
}
void move(int u,int v)//路径端点从u移到v 不考虑lca 
{
	int LCA=query_lca(u,v);
	while(u!=LCA)
		reverse(u),u=fa[u];
	while(v!=LCA)
		reverse(v),v=fa[v];
}
int pre[MAXN];//此次操作前,被操作的点本来的颜色 
void travel_ahead()//从cur-1转移到cur,执行cur的修改 
{
	int flag=0;
	if(vis[pos[cur]])
		flag=1,reverse(pos[cur]);//颜色变化了,需要重新计算贡献,移出修改颜色后再移回来 
	pre[cur]=col[pos[cur]];
	col[pos[cur]]=newx[cur];
	if(flag)
		reverse(pos[cur]);
}
void travel_back()//从cur+1转移到cur,撤销cur的修改 
{
	int flag=0;
	if(vis[pos[cur]])
		flag=1,reverse(pos[cur]);
	col[pos[cur]]=pre[cur];
	if(flag)
		reverse(pos[cur]);
}
void time_travel(int tar)
{
	while(cur<cntC && tim[cur+1]<=tar)
		++cur,travel_ahead();
	while(cur && tim[cur]>tar)
		travel_back(),--cur;
}
int main()
{
	n=read(),m=read(),Q=read();
	BlockSize=pow(n,0.666666666);
	for(int i=1;i<=m;++i)
		val[i]=read();
	for(int i=1;i<=n;++i)
		w[i]=read();
	for(int i=1;i<n;++i)
		{
			int u=read(),v=read();
			ins(u,v);
		}
	for(int i=1;i<=n;++i)
		col[i]=read();
	BuildBlocks();
	init_lca();
	for(int i=1;i<=Q;++i)
		{
			int op=read();
			assert(op==0 || op==1);
			if(op==1)
				{
					++cntQ;
					q[cntQ].u=read();
					q[cntQ].v=read();
					q[cntQ].id=cntQ;
					q[cntQ].tim=i;
				}
			else if(op==0)
				{
					++cntC;
					pos[cntC]=read();
					newx[cntC]=read();
					tim[cntC]=i;
				}
		}
	sort(q+1,q+cntQ+1);
//	cerr<<"sorted"<<endl;
	pu=pv=1;
	for(int i=1;i<=cntQ;++i)
		{
			time_travel(q[i].tim);
//			cerr<<"traveled"<<endl;
			move(pu,q[i].u);
			pu=q[i].u;
			move(pv,q[i].v);
			pv=q[i].v;
			int LCA=query_lca(pu,pv);
			reverse(LCA);
			Ans[q[i].id]=res;
			reverse(LCA);
		}
	for(int i=1;i<=cntQ;++i)
		printf("%lld\n",Ans[i]);
	return 0;
}

相关文章:

  • 2021-06-04
  • 2022-01-17
  • 2022-12-23
  • 2021-10-21
  • 2021-08-29
  • 2022-02-17
猜你喜欢
  • 2022-01-01
  • 2021-11-11
  • 2021-08-24
  • 2021-07-08
  • 2021-06-15
  • 2021-06-29
  • 2021-08-10
相关资源
相似解决方案