[BZOJ4448][Scoi2015]情报传递(树上主席树)
2016-09-25 17:22
351 查看
题目描述
传送门题解
一言不合写树套树。其实根本不需要嘎嘎嘎(而且据说ATP神犇空间M飞了。。。)
树上主席树就可以搞。
统计危险值大于ci的点的个数,实际上就是统计在i-ci之前开始收集情报的个数。也就是说,将每个点开始收集情报的时间建成一棵可持久化权值线段树之后,每次查询1~i-ci-1中有多少个点就行了。而最终的答案为sum[x]+sum[y]-sum[lca(x,y)]-sum[fa[lca(x,y)]]。
代码
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define N 200005 #define sz 18 int n,q,r,size,opt ,x ,y ,c ,ans; int tot,point ,nxt ,v ; int h ,father ,root ,t ,mi[sz+5]; int f [sz+5],sum[N*20],ls[N*20],rs[N*20]; void addedge(int x,int y) { ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; } void update(int &now,int l,int r,int x) { int mid=(l+r)>>1; int nxt=++size; sum[nxt]=sum[now]+1; ls[nxt]=ls[now]; rs[nxt]=rs[now]; now=nxt; if (l==r) return; if (x<=mid) update(ls[now],l,mid,x); else update(rs[now],mid+1,r,x); } int query(int now,int l,int r,int lrange,int rrange) { if (lrange>rrange) return 0; int mid=(l+r)>>1,ans=0; if (lrange<=l&&r<=rrange) return sum[now]; if (lrange<=mid) ans+=query(ls[now],l,mid,lrange,rrange); if (mid+1<=rrange) ans+=query(rs[now],mid+1,r,lrange,rrange); return ans; } void dfs(int x,int fa) { root[x]=root[fa]; if (t[x]) update(root[x],1,n,t[x]); h[x]=h[fa]+1; father[x]=fa; for (int i=1;i<sz;++i) { if (h[x]-mi[i]<1) break; f[x][i]=f[f[x][i-1]][i-1]; } for (int i=point[x];i;i=nxt[i]) { f[v[i]][0]=x; dfs(v[i],x); } } int lca(int x,int y) { if (h[x]<h[y]) swap(x,y); int k=h[x]-h[y]; for (int i=0;i<sz;++i) if (k&(1<<i)) x=f[x][i]; if (x==y) return x; for (int i=sz-1;i>=0;--i) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int main() { mi[0]=1; for (int i=1;i<=sz;++i) mi[i]=mi[i-1]*2; scanf("%d",&n); for (int i=1;i<=n;++i) { int fa; scanf("%d",&fa); if (!fa) r=i; else addedge(fa,i); } scanf("%d",&q); for (int i=1;i<=q;++i) { scanf("%d",&opt[i]); if (opt[i]==1) scanf("%d%d%d",&x[i],&y[i],&c[i]); else { int T; scanf("%d",&T); t[T]=i; } } dfs(r,0); for (int i=1;i<=q;++i) if (opt[i]==1) { int LCA=lca(x[i],y[i]),old=father[LCA]; ans=h[x[i]]+h[y[i]]-h[LCA]-h[old]; printf("%d ",ans); int k=i-c[i]-1; int ans1,ans2,ans3,ans4=0; ans1=query(root[x[i]],1,n,1,k); ans2=query(root[y[i]],1,n,1,k); ans3=query(root[LCA],1,n,1,k); if (old) ans4=query(root[old],1,n,1,k); ans=ans1+ans2-ans3-ans4; printf("%d\n",ans); } }
总结
①倍增dp的时候要从1开始搞,否则会把搞好的0清空掉。②注意权值线段树判断lrange和rrange的大小关系,如果为空的话直接返回0。
③lca的father有可能为0,也需要特判。
④写主席树和动态开点不要搞混了,动态开点是在原先的基础上更新,主席树每次新建一条链。
相关文章推荐
- vim常用命令
- JAVA虚拟机学习笔记(一)Windows10下编译OpenJDK8
- 深入理解Java的接口和抽象类
- java web笔记2
- Java多线程一:创建线程
- poj 1703 Find them, Catch them
- 【设计模式系列】--工厂方法
- Centos 7 系统安装完毕修改网卡名为eth0
- Hibernate延迟加载
- Hibernate二级缓存配置
- 多线程下单例模式:懒加载(延迟加载)和即时加载
- [bzoj 1013] [JSOI2008]球形空间产生器sphere:高斯消元
- pt-heartbeat
- LeetCode - 120. Triangle
- HDFS Truncate文件截断
- Kafka源码深度解析-序列6 -Consumer -消费策略分析
- 怎么投资理财,如果有且仅有100万本金?
- struts2的请求参数的编码和valuestack放在哪了
- 基于cocos2d-x引擎3.9版本分析之CCAutoreleasePool篇
- Hibernate入门案例及增删改查