原题不想说(太懒了),就说一下总结到的两点想法?
- 对于树上多次询问路径信息的问题,如果两条路径的信息无法快速合并(即不能 pushup),但是在路径两端增加/删除单点后的信息变化可以快速得出,可以试一下能不能用询问离线+类似换根 DP 的方法维护:钦定当前根为路径一端,使用数据结构维护以每个点作为路径另一端的答案。先钦定 \(1\) 为根,暴力求出 \(1\) 为路径一端时所有路径的答案,然后 dfs 换根,换根时考虑对答案造成的影响,在数据结构上修改。
- 对于多次询问某个区间 \([L,R]\) 的所有子区间 \([l,r]\) 信息和的问题,可以试一下离线,\(r\) 右移,对于每个 \(l\) 维护 \([l,r]\) 的信息,然后查询变为区间历史信息和。
本题使用上述思想维护即可做到 \(O((n+q)\log n)\),而且不需要使用树剖+暴力递归轻儿子等题解中说的麻烦的东西或者奇怪的科技(如果欧拉序不算的话)。
代码如下(非卡常版):
#include<bits/stdc++.h>
#define LN 17
#define N 100010
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
#define INF 0x7fffffff
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,Q;
int d[N],fa[N],size[N],son[N],top[N];
int nn,in[N],out[N],coef[N<<1];
ll ans[N];
vector<int>e1[N],e2[N];
vector<pii>q[N];
namespace Seg
{
const int NN=N<<3;
int scoef[NN];
int minn[NN],lazymin[NN],lazyres[NN];
ll res[NN];
inline void up(int k)
{
minn[k]=min(minn[k<<1],minn[k<<1|1]),scoef[k]=0;
if(minn[k]==minn[k<<1]) scoef[k]+=scoef[k<<1];
if(minn[k]==minn[k<<1|1]) scoef[k]+=scoef[k<<1|1];
res[k]=res[k<<1]+res[k<<1|1];
}
inline void build(int k,int l,int r)
{
if(l==r)
{
minn[k]=INF/2,scoef[k]=coef[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
up(k);
}
inline void downmin(int k,int v)
{
minn[k]+=v,lazymin[k]+=v;
}
inline void downres(int k,int v)
{
res[k]+=1ll*v*scoef[k];
lazyres[k]+=v;
}
inline void down(int k)
{
if(lazymin[k])
{
downmin(k<<1,lazymin[k]);
downmin(k<<1|1,lazymin[k]);
lazymin[k]=0;
}
if(lazyres[k])
{
if(minn[k]==minn[k<<1]) downres(k<<1,lazyres[k]);
if(minn[k]==minn[k<<1|1]) downres(k<<1|1,lazyres[k]);
lazyres[k]=0;
}
}
inline void update_put(int k,int l,int r,int x)
{
if(l==r)
{
minn[k]=0;
return;
}
down(k);
int mid=(l+r)>>1;
if(x<=mid) update_put(k<<1,l,mid,x);
else update_put(k<<1|1,mid+1,r,x);
up(k);
}
inline void update_minn(int k,int l,int r,int ql,int qr,int v)
{
if(ql<=l&&r<=qr)
{
downmin(k,v);
return;
}
down(k);
int mid=(l+r)>>1;
if(ql<=mid) update_minn(k<<1,l,mid,ql,qr,v);
if(qr>mid) update_minn(k<<1|1,mid+1,r,ql,qr,v);
up(k);
}
inline void update_res(int k,int l,int r,int ql,int qr,int v)
{
if(ql<=l&&r<=qr)
{
if(minn[k]==1) downres(k,v);
return;
}
down(k);
int mid=(l+r)>>1;
if(ql<=mid) update_res(k<<1,l,mid,ql,qr,v);
if(qr>mid) update_res(k<<1|1,mid+1,r,ql,qr,v);
up(k);
}
inline ll query(int k,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return res[k];
down(k);
int mid=(l+r)>>1;
ll sum=0;
if(ql<=mid) sum+=query(k<<1,l,mid,ql,qr);
if(qr>mid) sum+=query(k<<1|1,mid+1,r,ql,qr);
return sum;
}
}using Seg::update_put;using Seg::update_minn;using Seg::update_res;using Seg::query;
void dfs(int u)
{
in[u]=++nn,coef[nn]=1,size[u]=1;
for(int v:e1[u])
{
if(v==fa[u]) continue;
d[v]=d[u]+1,fa[v]=u;
dfs(v);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
out[u]=++nn,coef[nn]=-1;
}
void dfs1(int u,int tp)
{
top[u]=tp;
if(son[u]) dfs1(son[u],tp);
for(int v:e1[u])
if(v!=fa[u]&&v!=son[u])
dfs1(v,v);
}
inline int getlca(int a,int b)
{
while(top[a]!=top[b])
{
if(d[top[a]]<d[top[b]]) swap(a,b);
a=fa[top[a]];
}
if(d[a]>d[b]) swap(a,b);
return a;
}
inline int jump(int a,int f)
{
if(fa[a]==f) return a;
while(top[a]!=top[f])
{
if(fa[top[a]]==f) return top[a];
a=fa[top[a]];
}
return son[f];
}
inline bool insub(int a,int f)
{
return in[f]<=in[a]&&in[a]<=out[f];
}
inline void find(int u,int v,int x)
{
if(insub(u,v))
{
int son=jump(u,v);
update_minn(1,1,nn,1,in[son]-1,x);
update_minn(1,1,nn,out[son]+1,nn,x);
}
else update_minn(1,1,nn,in[v],out[v],x);
}
void solve(int u)
{
update_put(1,1,nn,in[u]);
update_put(1,1,nn,out[u]);
update_minn(1,1,nn,1,in[u],1);
update_minn(1,1,nn,out[u],nn,1);
int tmp=0;
while(tmp<(int)e2[u].size()&&in[e2[u][tmp]]<in[u])
find(u,e2[u][tmp],-1),tmp++;
update_res(1,1,nn,1,in[u],1);
update_res(1,1,nn,out[u],nn,1);
for(pii now:q[u])
{
const int v=now.fi,lca=getlca(u,v);
ans[now.se]=query(1,1,nn,1,in[u])+query(1,1,nn,1,in[v])-query(1,1,nn,1,in[lca])-(fa[lca]?query(1,1,nn,1,in[fa[lca]]):0);
}
for(int v:e1[u])
{
if(v==fa[u]) continue;
solve(v);
update_minn(1,1,nn,in[v],out[v],1);
while(tmp<(int)e2[u].size()&&in[e2[u][tmp]]<out[v])
find(u,e2[u][tmp],-1),tmp++;
update_res(1,1,nn,in[v],out[v],1);
}
if(u!=1)
{
update_res(1,1,nn,1,in[u]-1,-1);
update_res(1,1,nn,out[u]+1,nn,-1);
tmp=0;
while(tmp<(int)e2[u].size()&&in[e2[u][tmp]]<in[u])
find(u,e2[u][tmp],1),tmp++;
update_minn(1,1,nn,1,in[u]-1,-1);
update_minn(1,1,nn,out[u]+1,nn,-1);
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
n=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e1[u].push_back(v),e1[v].push_back(u);
}
d[1]=1,dfs(1),dfs1(1,1);
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e2[u].push_back(v),e2[v].push_back(u);
}
for(int i=1;i<=n;i++)
sort(e2[i].begin(),e2[i].end(),[&](int a,int b){return in[a]<in[b];});
Q=read();
for(int i=1;i<=Q;i++)
{
int u=read(),v=read();
if(in[u]>in[v]) swap(u,v);
q[v].push_back(mk(u,i));
}
Seg::build(1,1,nn);
solve(1);
for(int i=1;i<=Q;i++)
printf("%lld\n",ans[i]);
return 0;
}
标签:ch,return,XSY3549,int,top,Tree,son,换根,define
From: https://www.cnblogs.com/ez-lcw/p/16841019.html