【GMOJ4488】疯狂动物城
题目
题目链接:https://gmoj.net/senior/#main/show/4488
思路
其实思路并不难,就是一道码农题罢了 /fad。
对于一次询问 \(x,y\),我们设 \(\operatorname{lca}(x,y)=p\),我们把从 \(x\) 到 \(y\) 的道路拆成 \(x\to p\) 和 \(p\to y\) 两条(注意 \(p\) 被计算了两次,最后要减去一次 \(p\) 的贡献),然后分类讨论:
- 对于 \(x\to p\) 的点 \(k\),它到 \(y\) 的距离就是 \(dep[k]+dep[y]-2dep[p]\),设 \(t=dep[y]-2dep[p]\),那么它的贡献就是
\[\frac{a_k(dep[k]+t)(dep[k]+t+1)}{2}
\]
化简得
\[\frac{a_kdep[k]^2+a_kdep[k](2t+1)+a_k(t^2+t)}{2}
\]
- 对于 \(p\to y\) 的点 \(k\),它到 \(y\) 的距离就是 \(dep[y]-dep[k]\),设 \(t=dep[y]\),那么它的贡献就是
\[\frac{a_k(t-dep[x])(t-dep[x]+1)}{2}
\]
化简得
\[\frac{a_kdep[k]^2-a_kdep[k](2t+1)+a_k(t^2+t)}{2}
\]
我们发现,只需要在树上维护链 \(\sum a_k,\sum a_kdep[k],\sum a_kdep[k]^2\) 这三个信息,询问时乘上 \(t\) 即可。
为了方便,我们可以将所有信息乘上 \(2\),然后在输出的时候再将答案乘上 \(2\) 在 \(\bmod 20160501\) 时的逆元。通过计算可得到 \(\frac{1}{2}\equiv 10080251\pmod {20160501}\)。
然后就是树剖上主席树维护上面三个信息了。
区间修改时,我们要先预处理出 \(sumd[i][1/2]\) 表示在树剖之后,编号为 \(1\sim i\) 的点的深度的一次方 / 二次方和,然后区间修改就可以直接用 \(delta\) 乘上区间 \(sumd\)。
细节其实挺多的,写题时思路还是要清晰些。
然后由于我是菜鸡,求 \(\operatorname{LCA}\) 习惯性写了倍增 23333。
代码
// QuantAsk YYDS!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,LG=17,MOD=20160501,MAXN=N*LG*4,inv2=10080251;
int a[N],head[N],rt[N],dep[N],son[N],size[N],id[N],rk[N],top[N],f[N][LG+1],sumd[N][3];
int now,n,m,Q,tot,last,opt,X,Y,V;
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs1(int x,int fa)
{
f[x][0]=fa; dep[x]=dep[fa]+1; size[x]=1;
for (int i=1;i<=LG;i++)
f[x][i]=f[f[x][i-1]][i-1];
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa)
{
dfs1(v,x);
size[x]+=size[v];
if (size[v]>size[son[x]]) son[x]=v;
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp; id[x]=++tot; rk[tot]=x;
if (son[x]) dfs2(son[x],tp);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=f[x][0] && v!=son[x]) dfs2(v,v);
}
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=LG;i>=0;i--)
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=LG;i>=0;i--)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
struct SegTree
{
int tot,lc[MAXN],rc[MAXN],lazy[MAXN],sum[MAXN][3];
int New(int now)
{
int x=++tot;
lc[x]=lc[now]; rc[x]=rc[now]; lazy[x]=lazy[now];
sum[x][0]=sum[now][0]; sum[x][1]=sum[now][1]; sum[x][2]=sum[now][2];
return x;
}
void pushup(int x)
{
sum[x][0]=(sum[lc[x]][0]+sum[rc[x]][0])%MOD;
sum[x][1]=(sum[lc[x]][1]+sum[rc[x]][1])%MOD;
sum[x][2]=(sum[lc[x]][2]+sum[rc[x]][2])%MOD;
}
void pushdown(int now,int x,int l,int r)
{
if (lazy[x])
{
int mid=(l+r)>>1;
if (!lc[x] || lc[x]<x) lc[x]=New(lc[now]);
if (!rc[x] || rc[x]<x) rc[x]=New(rc[now]);
if (lc[x])
{
sum[lc[x]][0]=(sum[lc[x]][0]+1LL*lazy[x]*(mid-l+1))%MOD;
sum[lc[x]][1]=(sum[lc[x]][1]+1LL*lazy[x]*(sumd[mid][1]-sumd[l-1][1]))%MOD;
sum[lc[x]][2]=(sum[lc[x]][2]+1LL*lazy[x]*(sumd[mid][2]-sumd[l-1][2]))%MOD;
lazy[lc[x]]=(lazy[lc[x]]+lazy[x])%MOD;
}
if (rc[x])
{
sum[rc[x]][0]=(sum[rc[x]][0]+1LL*lazy[x]*(r-mid))%MOD;
sum[rc[x]][1]=(sum[rc[x]][1]+1LL*lazy[x]*(sumd[r][1]-sumd[mid][1]))%MOD;
sum[rc[x]][2]=(sum[rc[x]][2]+1LL*lazy[x]*(sumd[r][2]-sumd[mid][2]))%MOD;
lazy[rc[x]]=(lazy[rc[x]]+lazy[x])%MOD;
}
lazy[x]=0;
}
}
int build(int l,int r)
{
int x=++tot;
if (l==r)
{
sum[x][0]=a[rk[l]];
sum[x][1]=1LL*a[rk[l]]*dep[rk[l]]%MOD;
sum[x][2]=1LL*a[rk[l]]*dep[rk[l]]%MOD*dep[rk[l]]%MOD;
return x;
}
int mid=(l+r)>>1;
lc[x]=build(l,mid); rc[x]=build(mid+1,r);
pushup(x);
return x;
}
int update(int now,int x,int l,int r,int ql,int qr,ll v)
{
if (!x || x==now) x=New(now);
if (l==ql && r==qr)
{
sum[x][0]=(sum[x][0]+v*(r-l+1))%MOD;
sum[x][1]=(sum[x][1]+v*(sumd[r][1]-sumd[l-1][1]))%MOD;
sum[x][2]=(sum[x][2]+v*(sumd[r][2]-sumd[l-1][2]))%MOD;
lazy[x]=(lazy[x]+v)%MOD;
return x;
}
pushdown(now,x,l,r);
int mid=(l+r)>>1;
if (qr<=mid) lc[x]=update(lc[now],lc[x],l,mid,ql,qr,v);
else if (ql>mid) rc[x]=update(rc[now],rc[x],mid+1,r,ql,qr,v);
else lc[x]=update(lc[now],lc[x],l,mid,ql,mid,v),rc[x]=update(rc[now],rc[x],mid+1,r,mid+1,qr,v);
pushup(x);
return x;
}
ll query(int x,int l,int r,int ql,int qr,ll t,bool ff)
{
if (l==ql && r==qr)
if (ff) return (1LL*sum[x][0]*(t*t%MOD+t)+1LL*sum[x][1]*(t*2+1)+sum[x][2])%MOD;
else return (1LL*sum[x][0]*(t*t%MOD+t)-1LL*sum[x][1]*(t*2+1)+sum[x][2])%MOD;
pushdown(x,x,l,r);
int mid=(l+r)>>1;
if (qr<=mid) return query(lc[x],l,mid,ql,qr,t,ff);
if (ql>mid) return query(rc[x],mid+1,r,ql,qr,t,ff);
return (query(lc[x],l,mid,ql,mid,t,ff)+query(rc[x],mid+1,r,mid+1,qr,t,ff))%MOD;
}
}seg;
void Update(int x,int y)
{
while (dep[top[x]]>dep[y])
{
rt[m]=seg.update(rt[now],rt[m],1,n,id[top[x]],id[x],V);
x=f[top[x]][0];
}
rt[m]=seg.update(rt[now],rt[m],1,n,id[y],id[x],V);
}
ll Query(int x,int y,ll t,bool ff)
{
ll s=0;
while (dep[top[x]]>dep[y])
{
s=(s+seg.query(rt[now],1,n,id[top[x]],id[x],t,ff))%MOD;
x=f[top[x]][0];
}
return (s+seg.query(rt[now],1,n,id[y],id[x],t,ff))%MOD;
}
int main()
{
freopen("zootopia.in","r",stdin);
freopen("zootopia.out","w",stdout);
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&Q);
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
tot=now=0;
dfs1(1,0); dfs2(1,1);
for (int i=1;i<=n;i++)
{
sumd[i][1]=(sumd[i-1][1]+dep[rk[i]])%MOD;
sumd[i][2]=(sumd[i-1][2]+1LL*dep[rk[i]]*dep[rk[i]])%MOD;
}
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
rt[0]=seg.build(1,n);
while (Q--)
{
scanf("%d",&opt);
if (opt==1)
{
scanf("%d%d%d",&X,&Y,&V);
X^=last; Y^=last;
int p=lca(X,Y);
rt[++m]=0;
Update(X,p); Update(Y,p);
rt[m]=seg.update(rt[now],rt[m],1,n,id[p],id[p],-V);
now=m;
}
if (opt==2)
{
scanf("%d%d",&X,&Y);
X^=last; Y^=last;
int p=lca(X,Y);
last=(Query(X,p,dep[Y]-2LL*dep[p],1)+Query(Y,p,dep[Y],0)-seg.query(rt[now],1,n,id[p],id[p],dep[Y],0))*inv2%MOD;
last=(last+MOD)%MOD;
printf("%d\n",last);
}
if (opt==3)
{
scanf("%d",&now);
now^=last;
}
}
return 0;
}