P3571 [POI2014] SUP-Supercomputer 题解

P3571「POI2014」SUP-Supercomputer 题解

一道 “较” 水的黑题 (可一开始苦思冥想还是不会)

本蒟蒻的第一篇黑题题解,求赞。

题意简化

给定一棵 \(n\) 个节点、根节点为 \(1\) 的有根树。\(q\) 次询问中每次给定一个 \(k\),输出需要最少用几次操作次数 删除 完整棵树。每次操作可以选择 删除 不超过 \(k\) 个未访问的点,且这些点 没有父亲(血腥的味道)

前置知识

有一个 小小 的结论:存在一个 \(i\),满足可以用 \(i\) 次操作删掉所有深度小于等于 \(i\) 的点,剩下的操作每次都删掉 \(k\) 个点。

形式化地,设 \(f_k\)\(k\) 对应的答案,\(s_i\) 是深度大于 \(i\) 的点数,有:

\[f_k=\max_i \lceil\frac{s_i}{k}\rceil+i \]

  • Why?

显然 ,要证明 \(f_k=\max_i \lceil\frac{s_i}{k}\rceil+i\),转换为不等式,可分别证明 \(f_k\ge\max_i \lceil\frac{s_i}{k}\rceil+i\)\(f_k\le\max_i \lceil\frac{s_i}{k}\rceil+i\)

  1. 证明 \(f_k\ge\max_i \lceil\frac{s_i}{k}\rceil+i\)
    可以注意到一个性质,要删除至少一个深度为 \(i\) 的点,至少需要 \(i\) 次操作。那么有 \(f_k\ge \max_i \lceil\frac{s_i}{k}\rceil+i\)

  2. 证明 \(f_k\le\max_i \lceil\frac{s_i}{k}\rceil+i\)
    \(f_{k,i}=i+\lceil\frac{s_i}{k}\rceil\)\(f_{k,i}\)\(i=a\) 时取最大值。我们假设 \(b\) 步可以删除完前 \(b\) 层的节点,且这是满足条件的最大的 \(b\),即证 \(a=b\)

    • 先证 \(a\ge b\):对于 \(c<b\),若 \(f_{k,c}>f_{k,b}\),则深度范围在 \((c,b]\) 之间的点数大于 \(k(b−c)\),删掉一个第 \(c\) 层的点至少要 \(c\) 步,删掉 \(c+1\)\(b\) 层的所有点要大于 \((b−c)\) 步,那么前 \(b\) 层肯定 \(b\) 次删不完,矛盾。因此 \(a\ge b\)

    • 再证 \(a\le b\)

      1. \(b+1\) 层一定有超过 \(k\) 个节点,\(f_{k,b+1}\le f_{k,b}\)
      2. 若第 \(b+1\)\(b+2\) 层点数之和不超过 \(2k\),那么第 \(b+2\) 层的点数一定不足 \(b+1\) 层的,我们可以 \(b+2\) 次删除完前 \(b+2\) 层,矛盾,因此第 \(b+1\)\(b+2\) 层点数之和大于 \(2k\)\(f_{k,b+2}\le f_{k,b}\)

      以此类推 \(a\le b\)
      所以 \(a=b\),即 \(f_k\le\max_i \lceil\frac{s_i}{k}\rceil+i\)

根据上面对 \(a\le b\) 的证明,可以归纳证明第 \(b+1\) 到第 \(b+t\) 层的点数之和大于 \(kt\),于是我们只需要一层一层删掉,并优先删除有儿子的结点就一定可行。这样 \(f_k=\max_i \lceil\frac{s_i}{k}\rceil+i\),证毕。

题目解法

嘿嘿,大脑有没有烧了呢?有了以上结论,这道题就可以 切了。

\(f_k=\max_i \lceil\frac{s_i}{k}\rceil+i\) 进行变形:

\[f_k=\max_i \lceil\frac{s_i}{k}\rceil+i=f_k= \max_i \lceil\frac{s_i+ki}{k}\rceil \]

所以,只需求 \(g_k=\max s_i+ki\)

则:\(s_i=-ki+g_k\)\(y=kx+b\))。

斜率优化即可,横坐标和斜率都单调,复杂度 \(\mathcal{O}(n)\)

参考代码

	#include<bits/stdc++.h>
	using namespace std;
	const int N=1e6+5;
	int n,m,q[N],dep[N];
	int sum[N],maxn,ans[N];
	struct __{
		int x,y;
		bool operator<(const __ x)const{
			return y<x.y;
		}
	}a[N];
	double slp(int x,int y){
		if(x==y)return 1e9;
		return (sum[y+1]-sum[x+1])*1.0/(x-y);
	}
	signed main(){
		ios::sync_with_stdio(0);
		cin.tie(0); cout.tie(0);
		cin>>n>>m;sum[1]=dep[1]=1;
		for(int i=1;i<=m;i++)
			cin>>a[i].y,a[i].x=i;
		stable_sort(a+1,a+m+1);
		for(int i=2,x;i<=n;i++){
			cin>>x; dep[i] = dep[x]+1;
			maxn = max(maxn , dep[i]);
			sum[dep[i]]=sum[dep[i]]+1;
		}
		for(int i=maxn;i>0;i--)
			sum[i]+=sum[i+1];
		int l=1,r=1;
		for(int i=1;i<=maxn;i++){
			while(l<r&&slp(i,q[r])<=slp(q[r],q[r-1]))
				r--;
			q[++r]=i;
		}
		for(int i=1;i<=m;i++){
			while(l<r&&a[i].y>slp(q[l],q[l+1]))
				l++;
			int k=q[l],Y=a[i].y,X=a[i].x;
			ans[X] = k+(sum[k+1]+Y-1)/Y ;
		}
		for(int i=1;i<=m;i++)
			cout<<ans[i]<<" ";
		return 0;
	}

完 结 撒 花 ! ! !

posted @ 2024-10-19 15:46  夜·煞  阅读(13)  评论(0编辑  收藏  举报