[BZOJ3626][LNOI2014]LCA(离线+差分+树链剖分)
2016-05-12 17:13
477 查看
题目描述
传送门题解
首先把询问lr拆成两个询问。利用差分的思想,每次查询区间[1,i]与zlca的和,然后用r的值减去l-1的值就是答案。
查询区间[1,i]与zlca的和,可以将1-i所有的点到根的路径上的点都+1,然后查询z到根的路径上所有点的权值和即为答案。其实这就是深度的表示。
那么离线询问之后排序,时间复杂度为O(nlogn)
代码
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; const int max_n=5e4+5; const int max_e=max_n*2; const int max_tree=max_n*4; const int Mod=201314; int n,q,fa,l,r,z,N,u,t,cnt; int tot,point[max_n],nxt[max_e],v[max_e]; int size[max_n],top[max_n],h[max_n],father[max_n],son[max_n],num[max_n]; int sum[max_tree],delta[max_tree]; struct hp{int pt,z,id,f;}ask[max_e]; int ans[max_n][3]; inline void addedge(int x,int y) { ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; } inline int cmp(hp a,hp b) { return a.pt<b.pt; } inline void dfs_1(int x,int fa,int dep) { size[x]=1; h[x]=dep; father[x]=fa; int maxson=0; for (int i=point[x];i;i=nxt[i]) if (v[i]!=fa) { dfs_1(v[i],x,dep+1); size[x]+=size[v[i]]; if (size[v[i]]>maxson) { maxson=size[v[i]]; son[x]=v[i]; } } } inline void dfs_2(int x,int fa) { if (son[fa]==x) top[x]=top[fa]; else top[x]=x; num[x]=++N; if (son[x]) dfs_2(son[x],x); for (int i=point[x];i;i=nxt[i]) if (v[i]!=fa&&v[i]!=son[x]) dfs_2(v[i],x); } inline void update(int now) { sum[now]=sum[now<<1]+sum[now<<1|1]; } inline void pushdown(int now,int l,int r,int mid) { if (delta[now]) { sum[now<<1]+=delta[now]*(mid-l+1); delta[now<<1]+=delta[now]; sum[now<<1|1]+=delta[now]*(r-mid); delta[now<<1|1]+=delta[now]; delta[now]=0; } } inline void interval_change(int now,int l,int r,int lrange,int rrange,int v) { int mid=(l+r)>>1; if (lrange<=l&&r<=rrange) { sum[now]+=v*(r-l+1); delta[now]+=v; return; } pushdown(now,l,r,mid); if (lrange<=mid) interval_change(now<<1,l,mid,lrange,rrange,v); if (mid+1<=rrange) interval_change(now<<1|1,mid+1,r,lrange,rrange,v); update(now); } inline int query(int now,int l,int r,int lrange,int rrange) { int mid=(l+r)>>1,ans=0; if (lrange<=l&&r<=rrange) return sum[now]; pushdown(now,l,r,mid); if (lrange<=mid) ans+=query(now<<1,l,mid,lrange,rrange); if (mid+1<=rrange) ans+=query(now<<1|1,mid+1,r,lrange,rrange); return ans; } int main() { scanf("%d%d",&n,&q); for (int i=2;i<=n;++i) { scanf("%d",&fa); fa++; addedge(fa,i); } dfs_1(1,0,1); dfs_2(1,0); for (int i=1;i<=q;++i) { scanf("%d%d%d",&l,&r,&z); ++l; ++r; ++z; ask[++cnt].pt=l-1; ask[cnt].z=z; ask[cnt].id=i; ask[cnt].f=1; ask[++cnt].pt=r; ask[cnt].z=z; ask[cnt].id=i; ask[cnt].f=2; } sort(ask+1,ask+cnt+1,cmp); for (int i=1;i<=cnt;++i) { for (int j=ask[i-1].pt+1;j<=ask[i].pt;++j) { u=1; t=j; int f1=top[u],f2=top[t]; while (f1!=f2) { if (h[f1]<h[f2]) { swap(f1,f2); swap(u,t); } interval_change(1,1,N,num[f1],num[u],1); u=father[f1]; f1=top[u]; } if (num[u]>num[t]) swap(u,t); interval_change(1,1,N,num[u],num[t],1); } u=1; t=ask[i].z; int f1=top[u],f2=top[t]; while (f1!=f2) { if (h[f1]<h[f2]) { swap(f1,f2); swap(u,t); } ans[ask[i].id][ask[i].f]+=query(1,1,N,num[f1],num[u]); u=father[f1]; f1=top[u]; } if (num[u]>num[t]) swap(u,t); ans[ask[i].id][ask[i].f]+=query(1,1,N,num[u],num[t]); } for (int i=1;i<=q;++i) printf("%d\n",(ans[i][2]-ans[i][1]+Mod)%Mod); }
总结
想到了离线,想到了差分,想到了标记一个查询一个,但是没有想到深度标记的表示。相关文章推荐
- java的多态,instanceof,内部类,接口回调
- ecshop jquery冲突解决方法
- SpringMVC HelloWorld Restfull 项目
- nginx允许所有二级域名跨域请求
- JDK version
- Android的Button按钮,ACTION_UP事件不触发解决方案
- QP2
- hdu2577 How to Type(DP)
- Codeforces Round #352 (Div. 1) A. Recycling Bottles 暴力
- IOS 获取中英文字符串长度
- 在fragment中获取activity的组件
- [转]为什么你需要近距离接触Rust 1.0
- iOS数组的去重
- c# 执行javascript
- form提交表单到数据库
- 你了解border-radius吗?
- iOS 百度地图开发 怎么监听地图被拖动事件,然后怎么处理。
- Lua简明教程
- hdu2159 FATE(DP)
- ios 给不同按钮,动态绑定同一个点击事件,判断当前点击的按钮