洛谷3571 POI2014 SUP-Supercomputer (斜率优化)
一道神仙好题。
首先看到有多组\(k\),第一反应就是离线。
考虑贪心。
我们每次一定是尽量选择有儿子的节点。以便于我们下一次扩展。
但是对于一个\(k\),每次贪心的复杂度是\(O(n)\)
总复杂度是\(O(nq)\),肯定过不了。
qwq
那我们只能来考虑一个快速求一个\(k\)的答案。
感觉题解的柿子好神仙啊。
这里定义\(f[i]\)表示\(k=i\)的时候的最小次数。
\(sum[i]\)表示深度大于等于\(i\)的点有多少个。
则$$f[i]=max(j+\lceil \frac{sum[j+1]}{i} \rceil)$$
含义是用了\(i\)次把全\(i\)层都取掉,然后剩下的每次都能取满。
qwq现在来解释一下为什么是这个柿子。
首先,比较容易证明答案一定在所有的\((j+\lceil \frac{sum[j+1]}{i} \rceil\)中,因为这已经是最优情况了(前i次最多取i层,而后面也是每次取满,所以一定是最优的情况,不存在更优秀的情况。)
那么为什么是要取\(max\)呢。
是为了避免不合法的情况。
这里不合法的情况有两种情况,首先是前\(i\)次取不满\(前i层\)。那么如果存在这个情况,一定存在\(j\)使得,前\(j\)层能够\(j\)次取满,但是\(j到i\)不能用\(j-i\)取满,则存在等式
\]
\]
那么取\(max\),这种不合法的情况就能排除。
另一种不合法的情况就是,后面的次数并不能每次都取满。
如果上面的情况合法,那么一定存在$$\lceil \frac{sum[i+1]-sum[i+x+1]}{k} \rceil + i+x+1 > \lceil \frac{sum[i+1]}{k} \rceil + i$$
因为存在一层满足不能够一次用满k,且没有多余的东西让他选,那这时候等式左边的\(i+x+1\)等于该层的时候,一定比原本的柿子更大。
至于为什么不存在一个小于\(max\)并且合法的情况,是因为一层最少需要一次。而如果存在\(min\),说明要满足用小于\(j\)次,选完\(j\)层,而这个东西是不可能的。
qwq
那么证明到这里,大概能说明上述的柿子的是对的了。
也就是
\]
那接下来应该怎么优化呢。
我们将上述柿子进行变形
\]
那么如果存在一个\(j>k\)且\(j比k\)优秀的话,应该满足
\]
经过化简,$$\frac{sum[j+1]-sum[k+1]}{j-k}>-i$$
直接上斜率优化就好
\(sum\)数组可以直接通过前缀和求出来
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 1e6+1e2;
int sum[maxn];
int n,m;
int point[maxn],nxt[maxn],to[maxn];
int cnt;
int deep[maxn];
int dp[maxn];
int a[maxn];
void addedge(int x,int y)
{
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
}
struct Point{
ll x,y,num;
};
Point q[maxn];
ll chacheng(Point x,Point y)
{
return x.x*y.y-x.y*y.x;
}
bool Count(Point i,Point j,Point k)
{
Point x,y;
x.x=k.x-i.x;
x.y=k.y-i.y;
y.x=k.x-j.x;
y.y=k.y-j.y;
if (chacheng(x,y)>=0) return true;
return false;
}
int head=1,tail=0;
void push(Point x)
{
while(tail>=head+1 && Count(q[tail-1],q[tail],x)) tail--;
q[++tail]=x;
}
void pop(int lim)
{
while(tail>=head+1 && q[head+1].y-q[head].y>lim*(q[head+1].x-q[head].x)) head++;
}
int mx;
void dfs(int x,int dep)
{
deep[dep]++;
mx=max(mx,dep);
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
dfs(p,dep+1);
}
}
int qq;
int main()
{
n=read();qq=read();
int ymh =0;
for (int i=1;i<=qq;i++) a[i]=read(),ymh=max(ymh,a[i]);
for (int i=2;i<=n;i++)
{
int x=read();
addedge(x,i);
}
dfs(1,1);
for (int i=mx;i>=0;i--)
{
deep[i]+=deep[i+1];
}
for (int i=0;i<mx;i++)
push((Point){i,deep[i+1],i});
for (register int i=1;i<=ymh;++i)
{
pop((-1)*i);
int now = q[head].num;
dp[i]=now+((deep[now+1]-1)/i)+1;
}
for (int i=1;i<=qq;i++) cout<<dp[a[i]]<<" ";
return 0;
}
最新文章
- 换个角度看微信小程序[推荐]
- LeetCode 171 Excel Sheet Column Number
- 数据库的sacle-up和scale-out与sharding技术区分
- Spring mvc get和post传值乱码问题
- (三)映射对象标识符(OID)
- Silverlight中DataGrid的显示指定列、修改默认列名和格式化日期数据和小数数据
- Java实现配置加载机制
- 算法_Longest Palindromic Substring(寻找最长回文字串)
- Spark中的键值对操作
- idea 排除编译 参考
- Sonar 平台搭建及 Sonar 自定义规则打包部署篇
- Idea debug时报错:Command line is too long
- 【慕课网实战】二、以慕课网日志分析为例 进入大数据 Spark SQL 的世界
- Qt shortcuts
- react之异步请求数据,render先行渲染报错,未拿到数据
- ImportError: sys.meta_path is None, Python is likely shutting down
- 【ASP.NET 进阶】PDF文件在线预览(类似百度文库)
- linux下利用dd命令测试磁盘读写速度
- PostgreSQL——前言
- 加强树状数组luogu3368
热门文章
- Ubuntu18.04 + NVidia显卡 + Anaconda3 + Tensorflow-GPU 安装、配置、测试 (无需手动安装CUDA)
- uni-app 入门小白纯徒手编写组件 hello-popup
- HCNP Routing&;Switching之OSPF特殊区域
- 命令行解析函数:getopt_long、getopt
- docker学习笔记(二)--配置镜像加速器
- liunx常见指令
- AtCoder Regular Contest 069 D - Menagerie 枚举起点 模拟递推
- 搞不定 NodeJS 内存泄漏?先从了解垃圾回收开始
- SonarQube汉化
- JMeter多个线程组的使用说明