[WC2013] 糖果公园
III.[WC2013] 糖果公园
树上莫队。
树上莫队是依照于一种特殊的序列:括号序列而进行的莫队。括号序列,具体而言,是 dfs
树的时候,到一个点就插入序列,离开一个点的时候再插入一次。这样,所有在树上 路径上出现的点,在括号序列上就是自 第一次出现的位置到 第一次出现的位置,这一段上所有出现过恰一次的值——出现过零次的值显然不在考虑范围之内,而出现过两次的值是进了子树又出来的。可以开一个 vis
数组来表示每个点出现了奇数次还是偶数次。
但需要注意的是,若 LCA
并非两端点之一,它就不会被考虑到路径上,因此需要特判;若 不是 LCA
,它也不会被考虑,也需要特判。
然后剩下就是带修莫队板子了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int BBB=2200;
int n,m,q,a[100100],b[100100],c[100100],buc[100100],st[200100][20],fir[100100],LG[200100],tot,dep[100100],bra[200100],lef[200100],cnt,lim;
bool vis[100100];
ll res,ans[100100];
vector<int>v[100100];
void dfs(int x,int fa){
st[++tot][0]=x,fir[x]=tot,dep[x]=dep[fa]+1,bra[++cnt]=x,lef[x]=cnt;
for(auto y:v[x])if(y!=fa)dfs(y,x),st[++tot][0]=x;
bra[++cnt]=x;
}
int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
int LCA(int x,int y){
x=fir[x],y=fir[y];
if(x>y)swap(x,y);
int k=LG[y-x+1];
return MIN(st[x][k],st[y-(1<<k)+1][k]);
}
struct modify{int x,bef,aft;}r[100100];
struct query{
int x,y,z,id;
friend bool operator<(const query&u,const query&v){
if(lef[u.x]/BBB!=lef[v.x]/BBB)return lef[u.x]<lef[v.x];
if(lef[u.y]/BBB!=lef[v.y]/BBB)return lef[u.y]<lef[v.y];
return u.z<v.z;
}
}p[100100];
void Rev(int x){if(vis[x])res-=1ll*b[buc[c[x]]--]*a[c[x]];else res+=1ll*b[++buc[c[x]]]*a[c[x]];vis[x]^=1;}
int L,R,T;
void Tplus(){++T;if(vis[r[T].x])Rev(r[T].x),c[r[T].x]=r[T].aft,Rev(r[T].x);else c[r[T].x]=r[T].aft;}
void Tminus(){if(vis[r[T].x])Rev(r[T].x),c[r[T].x]=r[T].bef,Rev(r[T].x);else c[r[T].x]=r[T].bef;T--;}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
dfs(1,0);
for(int i=2;i<=tot;i++)LG[i]=LG[i>>1]+1;
for(int j=1;j<=LG[tot];j++)for(int i=1;i+(1<<j)-1<=tot;i++)st[i][j]=MIN(st[i][j-1],st[i+(1<<(j-1))][j-1]);
// for(int i=1;i<=cnt;i++)printf("%d ",bra[i]);puts("");
// for(int i=1;i<=n;i++)printf("%d ",lef[i]);puts("");
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
for(int i=1,t,x,y;i<=q;i++){
scanf("%d%d%d",&t,&x,&y);
if(!t)r[++lim].x=x,r[lim].bef=c[x],r[lim].aft=c[x]=y,i--,q--;
else{
if(lef[x]>lef[y])swap(x,y);
p[i].x=x,p[i].y=y,p[i].z=lim,p[i].id=i;
}
}
// for(int i=1;i<=lim;i++)printf("(%d,%d,%d)\n",r[i].x,r[i].bef,r[i].aft);
// for(int i=1;i<=q;i++)printf("[%d,%d,%d]\n",p[i].x,p[i].y,p[i].z);
L=1,R=0,T=lim;
sort(p+1,p+q+1);
for(int i=1;i<=q;i++){
while(L>lef[p[i].x])Rev(bra[--L]);
while(R<lef[p[i].y])Rev(bra[++R]);
while(L<lef[p[i].x])Rev(bra[L++]);
while(R>lef[p[i].y])Rev(bra[R--]);
while(T>p[i].z)Tminus();
while(T<p[i].z)Tplus();
int lca=LCA(p[i].x,p[i].y);
if(lca!=p[i].x&&lca!=p[i].y)Rev(lca);
if(p[i].x!=lca)Rev(p[i].x);
// printf("A:%d\n",p[i].id);
ans[p[i].id]=res;
if(p[i].x!=lca)Rev(p[i].x);
if(lca!=p[i].x&&lca!=p[i].y)Rev(lca);
}
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?