P4074 [WC2013]糖果公园 题解
这道题可以说是一道树上带修莫队的板子题。虽然评级是黑的,但是树上带修莫队本身还是比较好想的。就是代码很难调。
update 2021/4/12:现在评级掉紫了。
树上莫队:
树上莫队的本质就是利用欧拉序将树上莫队问题变成序列莫队问题。
我们设 表示欧拉序序列, 表示节点 在欧拉序中第一次出现与第二次出现的位置。那么当询问为 时(此处我们规定 的深度小于 ),如果 ,那么用 的区间,否则用 的区间同时带上 的贡献。为什么不是 ?因为中间出现二次的节点我们是不考虑的,因此用 相当于浪费时间 (用时间换空间的当我没说) 。这里千万注意:序列长度是 而不是 ,千万不要在这里 TLE 了!
带修莫队:
带修莫队的本质就是加一个时间轴,让指针除了在 上动还可以在 上动,将询问与修改分开储存,就可以完成了。
树上带修莫队:
实际上就是前面两个的结合体,先跑一遍欧拉序,然后再跑一遍带修莫队即可。
三个注意点:
- 在跑莫队的时候,如果要计算 的贡献(这里我们规定 不在询问的区间内,如果在可以前面直接先特判一下),算完之后一定不要忘记还原!
- 再次提醒:数列长度是 ,千万不能在这里 TLE 了!
- 这里块长取 ,比 要好一点,其中 表示欧拉序的长度
几个小优化:
- 如果可以,使用树链剖分求 。
吸氧。(经过实测,我一开始调的块长是 ,调错了,但是吸氧之后 )
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n,m,que,v[MAXN],w[MAXN],c[MAXN],fir[MAXN],las[MAXN],ys[MAXN],block,fa[MAXN][21],eular[MAXN<<1],dep[MAXN],cntq,cntc,vis[MAXN];
typedef long long LL;
LL ans[MAXN],total,cnt[MAXN];
vector<int>Next[MAXN];
struct query
{
int l,r,id,lca,Time;
}q[MAXN];
struct change
{
int pos,val;
}cha[MAXN];
int read()
{
int sum=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') {sum=(sum<<3)+(sum<<1)+(ch^48);ch=getchar();}
return sum;
}
void dfs(int x)
{
eular[++eular[0]]=x;
fir[x]=eular[0];
for(int i=0;i<Next[x].size();i++)
{
int u=Next[x][i];
if(dep[u]) continue;
dep[u]=dep[x]+1;
fa[u][0]=x;
dfs(u);
}
eular[++eular[0]]=x;
las[x]=eular[0];
}
void st()
{
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
int getlca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
int d=dep[x]-dep[y],tmp=-1;
while(d)
{
tmp++;int p=d&1;d>>=1;
if(p) x=fa[x][tmp];
}
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(const query &fir,const query &sec)
{
if(ys[fir.l]^ys[sec.l]) return ys[fir.l]<ys[sec.l];
if(ys[fir.r]^ys[sec.r]) return ys[fir.r]<ys[sec.r];
return fir.Time<sec.Time;
}
void add(int x)
{
total+=1ll*v[c[x]]*w[++cnt[c[x]]];
}
void del(int x)
{
total-=1ll*v[c[x]]*w[cnt[c[x]]--];
}
void work(int x)
{
vis[x]?del(x):add(x);
vis[x]^=1;
}
void deal(int t)
{
if(vis[cha[t].pos])
{
work(cha[t].pos);
swap(c[cha[t].pos],cha[t].val);
work(cha[t].pos);
}
else swap(c[cha[t].pos],cha[t].val);
}
int main()
{
n=read();m=read();que=read();
for(int i=1;i<=m;i++) v[i]=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
Next[x].push_back(y);
Next[y].push_back(x);
}
for(int i=1;i<=n;i++) c[i]=read();
fa[1][0]=1;dep[1]=1;dfs(1);st();block=ceil(pow(eular[0],2.0/3.0));
for(int i=1;i<=(n<<1);i++) ys[i]=(i-1)/block+1;
for(int i=1;i<=que;i++)
{
int opt=read(),zzh1=read(),zzh2=read();
if(opt)
{
q[++cntq].id=cntq;
q[cntq].Time=cntc;
if(fir[zzh1]>fir[zzh2]) swap(zzh1,zzh2);
int qlca=getlca(zzh1,zzh2);
if(zzh1==qlca) q[cntq].l=fir[zzh1],q[cntq].r=fir[zzh2];
else q[cntq].l=las[zzh1],q[cntq].r=fir[zzh2],q[cntq].lca=qlca;
}
else
{
cha[++cntc].pos=zzh1;
cha[cntc].val=zzh2;
}
}
sort(q+1,q+cntq+1,cmp);
int l=1,r=0,t=0;
for(int i=1;i<=cntq;i++)
{
while(l<q[i].l) work(eular[l++]);
while(l>q[i].l) work(eular[--l]);
while(r<q[i].r) work(eular[++r]);
while(r>q[i].r) work(eular[r--]);
while(t<q[i].Time) deal(++t);
while(t>q[i].Time) deal(t--);
if(q[i].lca) work(q[i].lca);
ans[q[i].id]=total;
if(q[i].lca) work(q[i].lca);
}
for(int i=1;i<=cntq;i++) printf("%lld\n",ans[i]);
return 0;
}
标签:
莫队,分块,根号分治,根号重构
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具