BZOJ 3626 [LNOI2014]LCA ——树链剖分
思路转化很巧妙。
首先把询问做差分。
然后发现加入一个点就把路径上的点都+1,询问的时候直接询问到根的路径和。
这样和原问题是等价的,然后树链剖分+线段树就可以做了。
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define ll long long #define mp make_pair #define md 201314 #define maxn 100005 int n,q,dep[maxn],data[maxn],ans[maxn]; namespace SegTree{ int sum[maxn<<3],dsum[maxn<<3],tag[maxn<<3]; void update(int o) { sum[o]=(sum[o<<1]+sum[o<<1|1])%md; } void build(int o,int l,int r) { if (l==r) { sum[o]=tag[o]=0; return ; } int mid=l+r>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); update(o); } void pushdown(int o,int l,int r) { if (tag[o]!=0) { int mid=l+r>>1; tag[o<<1]+=tag[o];tag[o<<1|1]+=tag[o]; sum[o<<1]+=tag[o]*(mid-l+1); sum[o<<1|1]+=tag[o]*(r-mid); tag[o]=0; } } int querysum(int o,int l,int r,int L,int R) { if (L<=l&&r<=R) return sum[o]; pushdown(o,l,r); int mid=l+r>>1; if (R<=mid) return querysum(o<<1,l,mid,L,R); else if (L>mid) return querysum(o<<1|1,mid+1,r,L,R); else return (querysum(o<<1,l,mid,L,R)+querysum(o<<1|1,mid+1,r,L,R))%md; } void modify(int o,int l,int r,int L,int R,int f) { if (L<=l&&r<=R) { sum[o]+=(r-l+1)*f; tag[o]+=f; return ; } pushdown(o,l,r); int mid=l+r>>1; if (R<=mid) return modify(o<<1,l,mid,L,R,f),update(o); else if (L>mid) return modify(o<<1|1,mid+1,r,L,R,f),update(o); else return modify(o<<1,l,mid,L,R,f),modify(o<<1|1,mid+1,r,L,R,f),update(o); } } namespace Tree{ int h[maxn],to[maxn],ne[maxn],en=0; int siz[maxn],son[maxn],dfn[maxn],top[maxn],fa[maxn],tot; int pos[maxn],id[maxn]; vector < pair<int,int> > v[maxn]; void add(int a,int b) { to[en]=b;ne[en]=h[a];h[a]=en++; } void dfs1(int o) { siz[o]=1; for (int i=h[o];i>=0;i=ne[i]) { dep[to[i]]=dep[o]+1; fa[to[i]]=o; dfs1(to[i]); siz[o]+=siz[to[i]]; if (siz[to[i]]>siz[son[o]]) son[o]=to[i]; } } void dfs2(int o,int tp) { top[o]=tp;pos[o]=++tot;id[tot]=o; if (!son[o]) return; dfs2(son[o],tp); for (int i=h[o];i>=0;i=ne[i]) if (to[i]!=son[o]) dfs2(to[i],to[i]); return ; } void build() { F(i,1,n) data[i]=dep[id[i]]; SegTree::build(1,1,n); } void add(int a,int b,int f) { while (top[a]!=top[b]) { if (dep[top[a]]<dep[top[b]]) swap(a,b); SegTree::modify(1,1,n,pos[top[a]],pos[a],f); a=fa[top[a]]; } if (dep[a]<dep[b]) swap(a,b); SegTree::modify(1,1,n,pos[b],pos[a],f); } int query(int a,int b) { int ret=0; while (top[a]!=top[b]) { if (dep[top[a]]<dep[top[b]]) swap(a,b); ret+=SegTree::querysum(1,1,n,pos[top[a]],pos[a]); ret%=md; a=fa[top[a]]; } if (dep[a]<dep[b]) swap(a,b); ret+=SegTree::querysum(1,1,n,pos[b],pos[a]); return ret%md; } void work() { F(i,1,q) { int l,r,z; scanf("%d%d%d",&l,&r,&z);l++;r++;z++; v[l-1].push_back(mp(z,-i)); v[r].push_back(mp(z,i)); } F(i,1,n) { add(1,i,1); for (int j=0;j<v[i].size();++j) { pair<int,int> pa=v[i][j]; if (pa.second<0) ans[-pa.second]-=query(1,pa.first); else ans[pa.second]+=query(1,pa.first); } } F(i,1,q) printf("%d\n",(ans[i]+md)%md); } } namespace Graph{ int h[maxn],to[maxn],ne[maxn],en=0; void add(int a,int b) {to[en]=b;ne[en]=h[a];h[a]=en++;} void dfs(int o,int fa) { if (fa) Tree::add(fa,o); for (int i=h[o];i>=0;i=ne[i]) if (to[i]!=fa) dfs(to[i],o); } } int main() { scanf("%d%d",&n,&q); memset(Tree::h,-1,sizeof Tree::h); memset(Graph::h,-1,sizeof Graph::h); F(i,2,n) { int fa; scanf("%d",&fa); Graph::add(fa+1,i); } Graph::dfs(1,0); dep[1]=1; Tree::dfs1(1); Tree::dfs2(1,1); Tree::build(); Tree::work(); }