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??
证一波吧。。
所有的除都是上取整
这个i+s[i]k的形式,意义是前i层用i次覆盖完了,剩下的除了最后一次选点,没有任何浪费。前i层用i步已经达到最优了,剩下的没有浪费,也是最优的,显然找到这个i后,这就是最优方案。
也就是说对于一个确定的k,现在要证明两件事
1、一定存在这样的i
2、i+s[i]k会在i处取得最大值

1、
考虑从最优选点策略入手
while(当前层点数<=k) {一次拿完这一层}
现在碰到了某层,能选的个数>k,我们设它为计算答案的那个i
设每个点到子树最深叶子节点的距离为h[x],这代表了拓展新节点的能力
我们不希望出现某次选完后下一次选不够k个造成浪费,所以应该选拓展能力最强的k个。
如果一个点被选后,他的所有孩子在下一次都没被选,这代表什么?
脑补一下,出过前k个范围的点的拓展能力会均匀的减小,最后所有都减到0。
在可选点个数小于k之前,如果一开始拓展能力最强的点出过前k个,那所有点的拓展能力都会均匀减小,最后全部减到0,这时s[i]k就是对的了。
如果没能全部选完,可选点数就小于k了。注意出过前k个的所有点拓展能力会均匀减小,所以剩下的从来都没有出过前k个。这代表什么?
设进行了a次操作,影响是我们把i+a层以前的全部消掉了,i+a层后的全部当前可用。(像这样)
这里写图片描述
这不是和我们刚选出i的局面相似么?其实i+a就是新的i!!!
循环这个过程,直到消完的那一次,我们就找到了正确的i。

2、
我们选出的是i,证明用j(j!=i)算答案时
i+s[i]k>=j+s[j]k
设g[i,j]表示i到j层有多少个点

如果j在i的上方
由于j次最多能取完前j层,而我们知道了i次必定能取完前i层,所以有
g[j+1,i]k<=ij
我们把g[j+1,i]这些点在i中贡献是i-j,拿去除一定不会变多

如果j在i下方
既然我们定下的是这个i,就代表前j次并不能拿完前j层。我们现在当做j次能拿完前j层,当然不会变差啦><

证完啦啦啦~~~
关于我一开始想的最优bfs序是不存在的。根据选点的最优策略,k不同时选点的顺序是不同的,也不是按bfs的顺序选的:)

0 0