bzoj3835

24 篇文章 0 订阅

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

#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 1100000
using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值