bzoj3835
来源:互联网 发布:最新卷皮淘宝客源码 编辑:程序博客网 时间:2024/06/16 05:29
题意:
给定一棵N个节点的有根树,根节点为1。
Q次询问,每次给定一个K,用最少的操作次数遍历完整棵树,输出最少操作次数。
每次操作可以选择访问不超过K个未访问的点,且这些点的父亲必须在之前被访问过。
N,K<=1000000
#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#include<algorithm>#define N 1100000using namespace std;struct node{int y,nex;}a[N];struct node1{int k,id;}q[N];struct node2{double x,y;}sta[N];int fir[N],len,n,m,ans[N],dep[N],s[N],tp,h;void ins(int x,int y){ a[++len].y=y;a[len].nex=fir[x];fir[x]=len;}void dfs(int x,int fa){ dep[x]=dep[fa]+1;h=max(h,dep[x]); s[dep[x]-1]++; for(int k=fir[x];k;k=a[k].nex) { int y=a[k].y; dfs(y,x); }}double get_k(node2 t1,node2 t2){ return (t1.y-t2.y)/(t1.x-t2.x);}bool cmp(node1 x,node1 y){ if(x.k<y.k) return 1; return 0;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) {scanf("%d",&q[i].k);q[i].id=i;} for(int i=2;i<=n;i++) {int x,y;scanf("%d",&x);y=i;ins(x,y);} dfs(1,0); for(int i=h;i>=1;i--) s[i]+=s[i+1]; for(int i=1;i<=h;i++) { node2 t=(node2){i,s[i]}; while(tp>1) { if(get_k(sta[tp-1],sta[tp])<get_k(sta[tp],t)) tp--; else break; } sta[++tp]=t; } sort(q+1,q+m+1,cmp); int j=1; for(int i=1;i<=m;i++) { double k=q[i].k; while(j<tp && get_k(sta[j],sta[j+1])>-k) j++; int t=sta[j].y; if(t%q[i].k) t+=q[i].k; t/=q[i].k; ans[q[i].id]=sta[j].x+t; } for(int i=1;i<m;i++) printf("%d ",ans[i]); printf("%d\n",ans[m]); return 0;}
题解:
这题太神了!
无视下一段就好:)
一开始我想找一个最优bfs序,就是无论k是多少,都按bfs序顺序取到最优值。对于每个点,在k>=某个值时由于跨层,他走的步数是一个定值,也就是会走到一个固定的点,称之为一条边。将询问和边排序后,加入边就用并查集处理。走到一个点x,如果他的边已经出现了,那就可以直接走到确定的一个fa[x],否则走到x+k。复杂度O(n log n)。然而不会证正确性也不知道怎么找bfs序。。
于是膜了题解
T^T??
证一波吧。。
所有的除都是上取整
这个
也就是说对于一个确定的k,现在要证明两件事
1、一定存在这样的i
2、
1、
考虑从最优选点策略入手
while(当前层点数<=k) {一次拿完这一层}
现在碰到了某层,能选的个数>k,我们设它为计算答案的那个i
设每个点到子树最深叶子节点的距离为h[x],这代表了拓展新节点的能力
我们不希望出现某次选完后下一次选不够k个造成浪费,所以应该选拓展能力最强的k个。
如果一个点被选后,他的所有孩子在下一次都没被选,这代表什么?
脑补一下,出过前k个范围的点的拓展能力会均匀的减小,最后所有都减到0。
在可选点个数小于k之前,如果一开始拓展能力最强的点出过前k个,那所有点的拓展能力都会均匀减小,最后全部减到0,这时
如果没能全部选完,可选点数就小于k了。注意出过前k个的所有点拓展能力会均匀减小,所以剩下的从来都没有出过前k个。这代表什么?
设进行了a次操作,影响是我们把i+a层以前的全部消掉了,i+a层后的全部当前可用。(像这样)
这不是和我们刚选出i的局面相似么?其实i+a就是新的i!!!
循环这个过程,直到消完的那一次,我们就找到了正确的i。
2、
我们选出的是i,证明用j(j!=i)算答案时
设g[i,j]表示i到j层有多少个点
如果j在i的上方
由于j次最多能取完前j层,而我们知道了i次必定能取完前i层,所以有
我们把g[j+1,i]这些点在i中贡献是i-j,拿去除一定不会变多
如果j在i下方
既然我们定下的是这个i,就代表前j次并不能拿完前j层。我们现在当做j次能拿完前j层,当然不会变差啦><
证完啦啦啦~~~
关于我一开始想的最优bfs序是不存在的。根据选点的最优策略,k不同时选点的顺序是不同的,也不是按bfs的顺序选的:)
- bzoj3835
- BZOJ3835: [Poi2014]Supercomputer
- BZOJ3835: [Poi2014]Supercomputer
- 四大组件之Activity
- java集合框架:ArrayList应用——去除ArrayList中的重复元素
- C#的protected internal
- Linux软件包管理
- 我为什么写博客
- bzoj3835
- 比较全的正则表达式总结
- 数据库字段设计之流程与状态
- OpenCV+OCR 图像处理字符识别原理及代码
- discuz学习记录:删除记录成功或者失败则返回json格式success=true或false
- 数据挖掘算法入门1——knn
- POJ 1703 Find them, Catch them(关系并查集)
- Java中集合Set的用法
- OpenGl中使用着色器的基本步骤及GLSL渲染简单示例