BZOJ3835: [Poi2014]Supercomputer

来源:互联网 发布:静态数据采集仪原理 编辑:程序博客网 时间:2024/05/22 20:29

题目大意:给定一棵N个节点的有根树,根节点为1。Q次询问,每次给定一个K,用最少的操作次数遍历完整棵树,输出最少操作次数。每次操作可以选择访问不超过K个未访问的点,且这些点的父亲必须在之前被访问过。

设s[i]表示深度大于i的点的个数

首先,我们需要知道,最优方案一定是先用i步拿完了前i层的点,然后再每次取k个,直到取完

证明如下:

首先我们先一层一层的拿,当这层的点大于k个时,我们挑选其中任意k个,当这层点的个数少于k个时,我们就可以拿前几层没拿的点,这样直到某一层的时候,满足了前面的点都被拿光,而之后有方案可以保证每次都能拿满k个,这一层就是最后的i

所以对于每一个k,相当于求max(i+s[i]k),1imaxdep
化简之后就变成了max(ki+s[i])k,1imaxdep
也就是说对于每一个k,只需要求出max(ki+s[i])就好了
我们可以把ix+s[i]当做一个一次函数,然后对于不同的i形成的这些一次函数维护一个凸壳,然后把k排好序在上面扫一遍就可以求出最大值了

#include<iostream>#include<cstdio>#define N 1000010using namespace std;int a[N];int to[N],nxt[N],pre[N],cnt;void ae(int ff,int tt){    cnt++;    to[cnt]=tt;    nxt[cnt]=pre[ff];    pre[ff]=cnt;}int d[N],fa[N],M;int c[N];void build(int x){    int i,j;    d[x]=d[fa[x]]+1;    M=max(M,d[x]);    c[d[x]]++;    for(i=pre[x];i;i=nxt[i])    {        j=to[i];        build(j);    }}int ans[N];int s[N],t;int main(){    int n,m;    scanf("%d%d",&n,&m);    int i,j,x,y;    for(i=1;i<=m;i++)    scanf("%d",&a[i]);    for(i=2;i<=n;i++)    {        scanf("%d",&fa[i]);        ae(fa[i],i);    }    build(1);    for(i=M-1;i>=1;i--)    c[i]+=c[i+1];    s[1]=1;s[2]=2;t=2;    for(i=3;i<=M;i++)    {        while(t>1&&(s[t]-s[t-1])*(c[i]-c[s[t-1]])-(c[s[t]]-c[s[t-1]])*(i-s[t-1])>=0) t--;        t++;s[t]=i;    }    int tmp=0;    cout<<endl;    for(i=1;i<=t;i++)    cout<<s[i]<<' '<<c[s[i]]<<endl;*/    j=1;    for(i=1;i<=n;i++)    {        while(j<t&&i*s[j]+c[s[j]]<i*s[j+1]+c[s[j+1]]) j++;        ans[i]=s[j];    }    for(i=1;i<=m;i++)    {        x=a[i];        if(x>=n) printf("%d",M);        else printf("%d",max(ans[x]-1+(c[ans[x]]+x-1)/x,M));        if(i!=m) putchar(' ');    }}
1 0
原创粉丝点击