题解:P16451 rvtmpq
lailai0916 · · 题解
题意简述
给定一棵带点权
解题思路
把树根定为
先考虑如何判断连通块与根链的交集。设
把区间内各点的 DFS 序看作一个值域区间查询。所求
按编号排列 DFS 序,记
对操作序列分块,块长取
重构时,需要一次求出每个点被块内修改覆盖的总系数。对任意点
设
还需得到每个
所有插入三元组只与树有关,可以在读入后预处理。每个编号经过轻边时才会再次插入,因此三元组总数为
预处理时间复杂度为
正确性证明
先证明根链交集的刻画。
再证明前驱、后继足以求出
接着证明重构公式。若
对固定的
小并大从重儿子的集合开始,依次插入其余编号。每次插入只会把原缺口
重构把上述系数乘以
参考代码
#include <bits/stdc++.h>
using namespace std;
using uint=uint32_t;
const int N=800005;
const int M=1<<20;
const int E=1600005;
const int K=20;
const int B=900;
const int C=(B+2)*(B+2);
const int inf=0x3f3f3f3f;
struct Insert
{
int pre,pos,nxt;
};
struct Update
{
int l,r,g;
uint v;
};
int head[N],to[E],nxt[E],ec;
int fa[N],dep[N],tin[N],tout[N],rev[N],ord[N];
int siz[N],son[N],stk[N],it[N],lg[N];
int st[K][N],zc[K][N],mid[K];
int mn[M<<1],mx[M<<1];
int li[N],ri[N];
int wx[N],wy[N],cx[B+5],cy[B+5];
uint a[N],b[N],s[N],sb[N],bad[N],neg[N];
uint mat[C];
size_t beg[N],cnt[N];
set<int> *ps[N];
vector<Insert> ins;
Update up[B+5];
int uc;
int n;
void add_edge(int u,int v)
{
to[++ec]=v;
nxt[ec]=head[u];
head[u]=ec;
}
void build_tree()
{
int top=1,tim=1;
stk[1]=1;
it[1]=head[1];
tin[1]=1;
rev[1]=ord[1]=1;
while(top)
{
int u=stk[top];
if(it[top])
{
int i=it[top];
it[top]=nxt[i];
int v=to[i];
if(v==fa[u])continue;
fa[v]=u;
dep[v]=dep[u]+1;
tin[v]=++tim;
rev[tim]=ord[tim]=v;
stk[++top]=v;
it[top]=head[v];
}
else
{
tout[u]=tim;
top--;
}
}
for(int i=n;i;i--)
{
int u=ord[i];
siz[u]++;
if(fa[u])
{
siz[fa[u]]+=siz[u];
if(siz[u]>siz[son[fa[u]]])son[fa[u]]=u;
}
}
for(int i=1;i<=n;i++)
{
int u=ord[i];
s[u]=s[fa[u]]+a[u];
sb[u]=sb[fa[u]]+b[u];
}
}
void build_lca()
{
lg[1]=0;
for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)st[0][i]=rev[i];
for(int j=1;j<K;j++)
{
int len=1<<(j-1);
for(int i=1;i+(len<<1)-1<=n;i++)
{
int u=st[j-1][i],v=st[j-1][i+len];
st[j][i]=dep[u]<dep[v]?u:v;
}
}
}
int lca(int u,int v)
{
int x=tin[u],y=tin[v];
if(x>y)swap(x,y);
if(x==y)return u;
x++;
int k=lg[y-x+1];
int p=st[k][x],q=st[k][y-(1<<k)+1];
return fa[dep[p]<dep[q]?p:q];
}
void build_range()
{
fill(mn,mn+(M<<1),inf);
for(int i=1;i<=n;i++)mn[M+i-1]=mx[M+i-1]=tin[i];
for(int i=M-1;i;i--)
{
mn[i]=min(mn[i<<1],mn[i<<1|1]);
mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
}
int range_lca(int l,int r)
{
l+=M-1;
r+=M-1;
int x=inf,y=0;
while(l<=r)
{
if(l&1){x=min(x,mn[l]);y=max(y,mx[l]);l++;}
if(!(r&1)){x=min(x,mn[r]);y=max(y,mx[r]);r--;}
l>>=1;
r>>=1;
}
return lca(rev[x],rev[y]);
}
void build_wavelet()
{
for(int i=0;i<n;i++)wx[i]=tin[i+1];
for(int k=K-1;k>=0;k--)
{
zc[k][0]=0;
for(int i=0;i<n;i++)zc[k][i+1]=zc[k][i]+!((wx[i]>>k)&1);
mid[k]=zc[k][n];
int l=0,r=mid[k];
for(int i=0;i<n;i++)
{
if((wx[i]>>k)&1)wy[r++]=wx[i];
else wy[l++]=wx[i];
}
swap(wx,wy);
}
}
int count_less(int l,int r,int x)
{
l--;
int res=0;
for(int k=K-1;k>=0;k--)
{
int zl=zc[k][l],zr=zc[k][r];
if((x>>k)&1)
{
res+=zr-zl;
l=mid[k]+l-zl;
r=mid[k]+r-zr;
}
else
{
l=zl;
r=zr;
}
}
return res;
}
int kth(int l,int r,int p)
{
l--;
int res=0;
for(int k=K-1;k>=0;k--)
{
int zl=zc[k][l],zr=zc[k][r];
int z=zr-zl;
if(p<=z)
{
l=zl;
r=zr;
}
else
{
p-=z;
l=mid[k]+l-zl;
r=mid[k]+r-zr;
res|=1<<k;
}
}
return res;
}
int prev_value(int l,int r,int x)
{
int p=count_less(l,r,x+1);
return p?kth(l,r,p):0;
}
int next_value(int l,int r,int x)
{
int p=count_less(l,r,x);
return p<r-l+1?kth(l,r,p+1):0;
}
int near(int u,int l,int r)
{
int x=prev_value(l,r,tin[u]);
int y=next_value(l,r,tin[u]);
int p=x?lca(u,rev[x]):0;
int q=y?lca(u,rev[y]):0;
if(!p)return q;
if(!q)return p;
return dep[p]>=dep[q]?p:q;
}
void insert_event(set<int> &q,int p)
{
auto z=q.lower_bound(p);
int x=z==q.begin()?0:*prev(z);
int y=z==q.end()?n+1:*z;
ins.push_back({x,p,y});
q.insert(z,p);
}
void build_events()
{
ins.reserve(static_cast<size_t>(n)*12);
for(int i=n;i;i--)
{
int u=ord[i];
beg[u]=ins.size();
ps[u]=son[u]?ps[son[u]]:new set<int>;
for(int j=head[u];j;j=nxt[j])
{
int v=to[j];
if(fa[v]!=u||v==son[u])continue;
for(auto k:*ps[v])insert_event(*ps[u],k);
delete ps[v];
}
insert_event(*ps[u],u);
cnt[u]=ins.size()-beg[u];
}
delete ps[1];
}
uint gap(int l,int r,int w)
{
return l>r?0:mat[li[l]*w+ri[r]];
}
void rebuild()
{
int m=uc;
if(!m)return;
uint sum=0;
for(int i=0;i<m;i++)
{
cx[i]=up[i].l;
cy[i]=up[i].r;
sum+=up[i].v;
}
sort(cx,cx+m);
sort(cy,cy+m);
int h=(int)(unique(cx,cx+m)-cx);
int z=(int)(unique(cy,cy+m)-cy);
int w=z+1;
fill(mat,mat+(h+1)*w,0);
for(int k=0;k<m;k++)
{
int i=(int)(lower_bound(cx,cx+h,up[k].l)-cx);
int j=(int)(lower_bound(cy,cy+z,up[k].r)-cy)+1;
mat[i*w+j]+=up[k].v;
}
for(int i=h-1;i>=0;i--)
{
for(int j=1;j<w;j++)mat[i*w+j]+=mat[(i+1)*w+j]+mat[i*w+j-1]-mat[(i+1)*w+j-1];
}
int j=0;
for(int i=1;i<=n+1;i++)
{
while(j<h&&cx[j]<i)j++;
li[i]=j;
}
j=0;
for(int i=0;i<=n;i++)
{
while(j<z&&cy[j]<=i)j++;
ri[i]=j;
}
fill(neg+1,neg+n+1,0);
for(int i=0;i<m;i++)if(up[i].g!=1)neg[fa[up[i].g]]-=up[i].v;
for(int i=n;i;i--)
{
int u=ord[i];
uint d=0;
for(size_t k=beg[u];k<beg[u]+cnt[u];k++)
{
auto t=ins[k];
d-=gap(t.pre+1,t.nxt-1,w);
d+=gap(t.pre+1,t.pos-1,w);
d+=gap(t.pos+1,t.nxt-1,w);
}
bad[u]=(son[u]?bad[son[u]]:sum)+d;
uint v=sum-bad[u]+neg[u];
a[u]+=b[u]*v;
if(fa[u])neg[fa[u]]+=neg[u];
}
for(int i=1;i<=n;i++)
{
int u=ord[i];
s[u]=s[fa[u]]+a[u];
}
uc=0;
}
uint query_point(int x)
{
uint res=a[x];
for(int i=0;i<uc;i++)
{
auto t=up[i];
if(tin[t.g]<=tin[x]&&tin[x]<=tout[t.g]&&near(x,t.l,t.r)==x)res+=b[x]*t.v;
}
return res;
}
uint query_path(int x)
{
uint res=s[x];
for(int i=0;i<uc;i++)
{
auto t=up[i];
if(tin[t.g]<=tin[x]&&tin[x]<=tout[t.g])
{
int y=near(x,t.l,t.r);
res+=t.v*(sb[y]-sb[fa[t.g]]);
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
add_edge(u,v);
add_edge(v,u);
}
build_tree();
build_lca();
build_range();
build_wavelet();
build_events();
uint last=0;
int cur=0;
while(q--)
{
int op;
cin>>op;
if(op==1)
{
uint l,r,v;
cin>>l>>r>>v;
l^=last;
r^=last;
v^=last;
int x=(int)l,y=(int)r;
up[uc++]={x,y,range_lca(x,y),v};
}
else if(op==2)
{
uint x;
cin>>x;
x^=last;
last=query_point((int)x);
cout<<last<<'\n';
}
else if(op==3)
{
uint x;
cin>>x;
x^=last;
last=query_path((int)x);
cout<<last<<'\n';
}
if(++cur==B){rebuild();cur=0;}
}
return 0;
}