[DS记录]P6779 [Ynoi2009] rla1rmdq
command_block
2021-06-28 20:04:13
**题意** : 给定一棵 $n$ 个点的树,边有边权。
维护长为 $n$ 的序列 $A$ ,支持下列操作 :
- 对于 $i\in[l,r]$ ,令 $A_i$ 变为 $fa(A_i)$。(特殊地,定义 $fa(rt)=rt$)
- 查询 $\min_{i\in[l,r]}dep(A_i)$
允许离线, $n,m\leq 2\times 10^5$ ,时限$\texttt{3s}$ ,空限$\texttt{64Mb}$。
------------
分块。
对于每个块记 $tag$ 为整块一起上跳的次数。
对于修改或查询中的散块,暴力计算并将 $tag$ 置为 $0$。
这需要支持快速求树上 $k$ 级祖先,可以使用重剖,额外代价为 $O(n\log n)$ ,除此之外每次询问可以看做 $O(1)$。
接下来只需考虑全局修改和询问的子问题,用于处理整块。
- 观察 :若某个关键点 $u$ 的祖先存在其他关键点,可以忽略 $u$ 的贡献。
于是,在树上记录该点是否被经过,若某个关键点的父亲被经过,则可以将其删除。每个点只会被经过一次,这样只会移动 $O(n)$ 次。
注意,在重构块时,原先被删除的点可能再次跻身候选点之列。
对于删除的点,要打上时间戳,这样在暴力重构时就可以知道要跳多少步。
逐块处理以节省空间。时间复杂度 $O(m\sqrt{n})$ ,空间复杂度 $O(n+m)$。
```cpp
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#define pb push_back
#define ll long long
#define MaxN 200500
using namespace std;
const ll INF=1ll<<60;
vector<int> g[MaxN],l[MaxN];
int siz[MaxN],dep[MaxN],fa[MaxN],tf[MaxN],mp[MaxN];
ll dis[MaxN];
void pfs1(int u)
{
siz[u]=1;
for (int i=0,v;i<g[u].size();i++)
if (!siz[v=g[u][i]]){
dep[v]=dep[fa[v]=u]+1;
dis[v]=dis[u]+l[u][i];
pfs1(v);
siz[u]+=siz[v];
if (siz[v]>siz[mp[u]])
mp[u]=v;
}
}
int dfn[MaxN],tp[MaxN],tim;
void pfs2(int u,int _tf)
{
tp[dfn[u]=++tim]=u;
tf[u]=_tf;
if (mp[u])pfs2(mp[u],_tf);
for (int i=0;i<g[u].size();i++)
if (!tf[g[u][i]])
pfs2(g[u][i],g[u][i]);
}
int rt;
int trans(int u,int k)
{
if (k>=dep[u])return rt;
k=dep[u]-k;
while(k<dep[tf[u]])u=fa[tf[u]];
return tp[dfn[u]-dep[u]+k];
}
int BS,a[MaxN],now[MaxN],tag,s[666],top;
bool vis[MaxN],flag;ll buf;
void add(int l,int r,int k)
{
int bl=k*BS,br=bl+BS;
if (r<bl||br<=l)return ;
if (l<=bl&&br-1<=r){
int tn=0;tag++;
for (int i=1;i<=top;i++){
int p=s[i];
if (a[p]==rt)flag=1;
else if (!vis[fa[a[p]]]){
vis[a[p]=fa[a[p]]]=1;
buf=min(buf,dis[a[p]]);
now[s[++tn]=p]++;
}
}top=tn;
return ;
}
for (int i=bl;i<br;i++){
a[i]=trans(a[i],tag-now[i]);
now[i]=tag;
}
l=max(l,bl);r=min(r,br-1);
int tn=0;
for (int i=1;i<=top;i++)
if (s[i]<l||r<s[i])s[++tn]=s[i];
top=tn;
for (int i=l;i<=r;i++){
a[i]=fa[a[i]];
if (a[i]==rt)flag=1;
else if (!vis[a[i]])
vis[a[s[++top]=i]]=1;
}
for (int i=bl;i<br;i++)buf=min(buf,dis[a[i]]);
}
ll qry(int l,int r,int k)
{
int bl=k*BS,br=bl+BS;
if (r<bl||br<=l)return INF;
ll ret=INF;
if (l<=bl&&br-1<=r)return buf;
for (int i=bl;i<br;i++){
a[i]=trans(a[i],tag-now[i]);
now[i]=tag;
}
l=max(l,bl);r=min(r,br-1);
for (int i=l;i<=r;i++)ret=min(ret,dis[a[i]]);
return ret;
}
struct Data{int op,l,r,p;}b[MaxN];
int n,m,q;ll ans[MaxN];
int main()
{
scanf("%d%d%d",&n,&m,&rt);
for (int i=1,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
g[u].pb(v);g[v].pb(u);
l[u].pb(w);l[v].pb(w);
}dep[rt]=1;fa[rt]=rt;pfs1(rt);pfs2(rt,rt);
BS=0.8*sqrt(n)+1;
for (int i=0;i<n;i++)scanf("%d",&a[i]);
for (int i=n;i<n/BS*BS+BS;i++)a[i]=1;
for (int i=1;i<=m;i++){
scanf("%d%d%d",&b[i].op,&b[i].l,&b[i].r);
b[i].l--;b[i].r--;
if (b[i].op==2)b[i].p=++q;
}for (int i=1;i<=q;i++)ans[i]=INF;
for (int k=0;k*BS<n;k++){
memset(vis,0,sizeof(bool)*(n+5));
top=BS;tag=0;buf=INF;
int bl=k*BS,br=bl+BS;
for (int i=1;i<=BS;i++)vis[a[s[i]=bl+i-1]]=1;
for (int i=bl;i<br;i++)buf=min(buf,dis[a[i]]);
for (int i=1;i<=m;i++){
if (b[i].op==1)add(b[i].l,b[i].r,k);
else ans[b[i].p]=min(ans[b[i].p],qry(b[i].l,b[i].r,k));
}
}for (int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}
```
明天也要开心哦~