考场

乍一看 T1 像是二分答案,手玩样例发现可以 \(O(k^2)\) 枚举点对,贪心地更新答案,完了?有点不信,先跳了

T2 的形式有点像逆序对,但没啥想法

T3 的式子完全不知道如何处理,一看就是乱搞的题

回来看 T1,原来的想法果然是假的

悲伤的是思考用了 1h10min,导致开始写代码是有点紧张,怕写不完

T2 先尝试了一个假的贪心,过了样例但小数据都过不了拍,又尝试用贪心来剪枝,发现不可行。

T3 写完暴力尝试把几个贪心拼起来:从根往下选,从父亲往上选,选 \(c\) 最小的,随机选,选父亲的答案(现在一想可以随机选根到父亲的答案),根据时空限制调了调参数,大概 17.00 丢下。

T1 又尝试了维护右凸包、类似平面图转对偶图的建图(可惜当时一直想从左到右跑最短路,没有考虑从上到下),还是老老实实二分吧。check 时枚举每个点,看上下会不会形成“屏障”(能不能走通),同样没想到从上边界往下边界搜。

最后剩 5min 才开始交题,网站还卡了,以后要早点。感觉自己考得很差,很慌。

res

spj 和数据上出大锅了,导致 T3 有一堆人爆 \(0\)(包括我,因此 rk10+),机房里高声赞美出题人。没想到晚饭后 T3 修复 spj 重测了。

rk3 40+20+50

T1 WA了一个点,首先是算距离是平方爆 int 了,其次屏障不一定长得是从上到下,可能会“拐弯”。\(O(k^2\log\frac m\epsilon)\) 能拿 80pts

T3 一分都没有骗到。。。

rk1 杨卓凡 80+20+30

rk3 赵思远 10+20+80

rk5 彭乙桐 10+40+50

题解

说实话,官方和大部分网上的题解都质量低下,写得很含糊,代码也没有注释,但这套题不论是思维还是代码都很好,这里详细写一下题解。

Star Way To Heaven

80pts 做法

把上下边界也看作两点。

有两种理解:

  1. 点集的每个子集都会构成一个屏障,显然每个屏障选择穿过最长的边,答案即为所有最长边的最小值。考虑直接求出最小的屏障,那么从上边界开始,每次把距离当前点集最近的点加入点集,下边界被加入点集时结束。发现这个算法就是 Prim
  2. 相当于求从上边界到下边界的最小瓶颈路,Prim 求 MST
code
const int N = 6005;
int n,m,k;
double x[N],y[N]; double ans,d[N];
bool vis[N]; double dis(int i,int j)
{ return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); } signed main() {
scanf("%d%d%d",&n,&m,&k);
For(i,1,k) scanf("%lf%lf",&x[i],&y[i]), d[i] = m-y[i];
d[0] = 2e9, d[k+1] = m;
for(;;) {
int u = 0;
For(i,1,k+1) if( !vis[i] && d[i] < d[u] ) u = i;
vis[u] = 1, ans = max(ans,d[u]);
if( u == k+1 ) break;
For(i,1,k) d[i] = min(d[i],dis(u,i));
d[k+1] = min(d[k+1],y[u]);
}
printf("%.8lf",ans/2);
return 0;
}

God Knows

考虑 DP。设 \(f[i]\) 为选 \((i,p[i])\) 且前 \(i\) 个点的连线均已删的最小代价,转移时枚举 \(j\in[0,i)\),要求 \(i,j\) 之间的连线与 \((i,p[i])\) 或 \((j,p[j])\) 相交:

\[f[i]=min(f[i],f[j]+c[i]),\forall k\in(j,i)\ ,p[k]<p[j]或p[k]>p[i]
\]

下文代码是 \(O(n^3)\) 的,但通过预处理可以优化到 \(O(n^2)\)。

40pts
const int N = 2e5+5;
int n,p[N],c[N]; int f[N],ans=0x3f3f3f3f; bool check(int l,int r) {
if( p[l] > p[r] ) return 0;
For(i,l,r) if( p[l] < p[i] && p[i] < p[r] ) return 0;
return 1;
} signed main() {
read(n);
For(i,1,n) read(p[i]);
For(i,1,n) read(c[i]);
mem(f,0x3f,n);
f[0] = 0;
For(i,1,n) For(j,0,i-1)
if( check(j,i) ) f[i] = min(f[i],f[j]+c[i]);
for(int i = n, j = 0; i && p[i] >= p[j]; --i) {
ans = min(ans,f[i]);
j = max(j,p[i]);
}
write(ans);
return ioclear();
}

把 \((i,p[i])\) 看作二维坐标系中的点,考虑转移方程中条件的几何意义:以 \((i,p[i]),(j,p[j])\) 为对角的矩形中没有其他点(合法的 \(j\) 构成了右半个上凸包)。尝试用数据结构求 \(\min\{f[j]\}\),但条件中 \(p[k]\) 有两种合法情况很烦,考虑交换 \(x,y\) 坐标。以 \(p\) 为下标建线段树,以 \(p\) 递减的顺序查询,那么 \(j\) 应递增(从右往左查这个 \(\frac 14\) 凸包)。写法类似李超线段树,时间复杂度 \(O(n\log^2n)\)。

100pts
const int N = 2e5+5, inf = 0x3f3f3f3f;
int n,p[N]; int res,now; // now: 当前的j。左边的j要大于它
struct Node {
int l,r,mx,lmn,f;
// mx: 最大的i;
// lmn: 预处理左半区间的答案(插入变为\log^2n,否则calc会变成n\logn)
} t[N*4]; #define ls (u<<1)
#define rs (u<<1|1)
void build(int u,int l,int r) {
t[u].l = l, t[u].r = r, t[u].lmn = inf;
if( l == r ) return;
int mid = l+r>>1;
build(ls,l,mid), build(rs,mid+1,r);
}
int calc(int u,int qr) { // 求t[u].l<=p[j]<=t[u].r,j>qr的min{f[j]}
if( t[u].l == t[u].r ) return t[u].mx > qr ? t[u].f : inf;
if( t[rs].mx >= qr ) return min(t[u].lmn,calc(rs,qr));
return calc(ls,qr);
}
void insert(int u,int x,int y,int f) { // (p[i]=x,i=y),f[i]=f
if( t[u].l == t[u].r ) { t[u].mx = y, t[u].f = f; return; }
insert( x<=t[ls].r?ls:rs ,x,y,f);
t[u].mx = max(t[ls].mx,t[rs].mx), t[u].lmn = calc(ls,t[rs].mx);
}
void query(int u,int x) {
if( t[u].r <= x ) {
res = min(res,calc(u,now)), now = max(now,t[u].mx);
return;
}
if( t[rs].l <= x ) query(rs,x);
query(ls,x); // 顺序不能反
} signed main() {
read(n);
For(i,1,n) read(p[i]);
build(1,1,n);
For(i,1,n) {
int c; read(c);
res = inf, now = 0, query(1,p[i]);
insert(1,p[i],i,(res==inf?0:res)+c);
}
res = inf, now = 0, query(1,n);
write(res);
return ioclear();
}

对代码的解释可以看 ys的博客

Lost My Music

把式子化成

\[-\frac{c[u]-c[v]}{dep[u]-dep[v]}
\]

那么把每个点看作二维坐标系上 \((dep[i],c[i])\),忽略负号的话就是求斜率的最大值,维护下凸包

写法有两种:

一种是倍增,设 \(f[u][0]\) 为 \(u\) 的祖先中,下凸包上 \(u\) 的前一个点。

另一种是单调栈上二分,用到了树上还原栈的技巧。

倍增
const int N = 5e5+5;
int n,c[N],fa[N];
vector<int> to[N]; int dep[N],f[N][19]; LD K(int u,int v) { return LD(c[u]-c[v])/(dep[u]-dep[v]); } void dfs(int u) {
dep[u] = dep[fa[u]] + 1;
int p = fa[u];
rFor(i,17,0) if( f[p][i] > 1 )
if( K(f[f[p][i]][0],f[p][i]) > K(f[p][i],u) ) p = f[p][i];
if( p != 1 && K(f[p][0],u) > K(p,u) ) p = f[p][0];
f[u][0] = p;
For(i,1,18) f[u][i] = f[f[u][i-1]][i-1];
for(int v : to[u]) dfs(v);
} signed main() {
read(n);
For(i,1,n) read(c[i]);
For(i,2,n) read(fa[i]), to[fa[i]].pb(i);
dfs(1);
For(i,2,n) printf("%.10Lf\n",-K(f[i][0],i));
return ioclear();
}
二分 by hkh
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1100000;
long double ans[N];
int dep[N],head[N],cnt,zhan[N];
int n;
ll c[N];
struct bian
{
int nxt,to;
}b[N];
void add(int u,int v)
{
b[++cnt].to=v;
b[cnt].nxt=head[u];
head[u]=cnt;
}
long double K(int x,int y)
{
return (long double)(c[x]-c[y])/(long double)(dep[x]-dep[y]);
}
int get(int u,int r)
{
int l=2,mid;
long double k1,k2;
while(l<=r)
{
mid=(l+r)>>1;
k1=(long double)(-1)*K(zhan[mid],zhan[mid-1]);
k2=(long double)(-1)*K(zhan[mid],u);
if(k1<k2)
r=mid-1;
else
l=mid+1;
}
return l-1;
}
void dfs(int u,int fa,int top)
{
int k=get(u,top)+1,tmp1=zhan[k],tmp2=zhan[k-1];
if(u==1)
k=1;
ans[u]=(long double)(-1)*K(tmp2,u);
zhan[k]=u;
for(int i=head[u],v;i;i=b[i].nxt)
{
v=b[i].to;
if(v==fa)
continue;
dep[v]=dep[u]+1;
dfs(v,u,k);
}
zhan[k]=tmp1;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&c[i]);
}
for(int i=2,a;i<=n;i++)
{
scanf("%d",&a);
add(a,i);
}
dep[1]=1;
dfs(1,0,0);
for(int i=2;i<=n;i++)
{
printf("%.10Lf\n",ans[i]);
}
return 0;
}

20210715 noip16的更多相关文章

  1. [Luogu 1850] noip16 换教室

    [Luogu 1850] noip16 换教室 好久没有更博客了,先唠嗑一会,花了两天的空闲时间大致做完了昨年的noip真题 虽然在经过思考大部分题目都可出解(天天爱跑步除外),但是并不知道考试时候造 ...

  2. 【NOIP16提高组】换教室

    [题目链接] 点击打开链接 [算法] 概率DP 先跑一遍floyed,求出每个教室之间的最短路径,存在数组dist[][]中,时间复杂度O(V^3) 设计状态,f[i][j][k]表示当前选到第i个教 ...

  3. noip16

    <凉宫春日的忧郁>专场 T1 考试的时候连题面都没看懂,都没往图论这方面想,更别提最小生成树. 正解: 最小生成树prim,好像是什么欧几里得生成树,寒假时候的东西了,我直接找的blog看 ...

  4. 20210716考试-NOIP16

    考场时Prim的 $i$ 写成 $k$ 100->0 rank1->rank23 T1 Star Way To Heaven 考场正解:假设你要二分答案,则几个圆组成几道"屏障& ...

  5. 调用免费API查询全年工作日、周末、法定节假日、节假日调休补班数据

    前言 日常开发中,难免会用到判断今天是工作日.周末.法定节假日.节假日调休补班做一些业务处理,例如:仅在上班时间给用户推送消息.本文记录调用免费API查询全年工作日.周末.法定节假日.节假日调休补班数 ...

  6. Java 中节省 90% 时间的常用的工具类

    前言 你们有木有喜欢看代码的领导啊,我的领导就喜欢看我写的代码,有事没事就喜欢跟我探讨怎么写才最好,哈哈哈...挺好. 今天我们就一起来看看可以节省 90% 的加班时间的第三方开源库吧,第一个介绍的必 ...

  7. Java流程控制01——用户交互Scanner

    用户交互Scanner sacnner对象 之前的语法并没有实现程序与人的交互.java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入. 基本语法:  S ...

  8. Python小白的数学建模课-19.网络流优化问题

    流在生活中十分常见,例如交通系统中的人流.车流.物流,供水管网中的水流,金融系统中的现金流,网络中的信息流.网络流优化问题是基本的网络优化问题,应用非常广泛. 网络流优化问题最重要的指标是边的成本和容 ...

  9. centos7系统上pgsql的一些报错解决方法

    1.2021-07-15 # 问题: 登录时服务器拒绝连接 psql -h 192.168.1.112 # 解决方法:修改配置文件 pg_hba.conf ,将该主机加进白名单 vi pg_hba.c ...

随机推荐

  1. 解决iOS上网页滑动不流畅问题

    body { overflow:auto; /* 用于 android4+,或其他设备 */ -webkit-overflow-scrolling:touch; /* 用于 ios5+ */ }说明: ...

  2. 什么是软件的CLI安装

    Websoft9 在进行开源软件的集成与自动化安装研究过程中发现有些软件有CLI安装模式,例如Gitlab CLI版本.Ghost CLI.PHP CLI等,CLI安装是什么意思? CLI(Comma ...

  3. 题解AGC004C

    题目 . 样例 AGC 好评. 题意:让你在一个 \(H \times W\) 的方格纸上找两个连通块,使得他们的重合部分就是输入中给的部分. 先放个样例. 输入: 5 5 ..... .#.#. . ...

  4. dubbo学习实践(3)之Dubbo整合Consul及Dubbo配置方式

    前言:上一篇中,已经写到了使用zookeeper为注册中心的配置,下面写下配置Consul为注册中心 1. Consul注册中心验证 修改provider和consumer的服务配置文件 Provid ...

  5. 【笔记】KNN之分类准确度

    分类准确度 分类准确度 以sklearn中的手写数字datasets.load_digits为例,其是8*8的图形,具有64个特征值,类别由0到9 我们挑选出666这个图形,将其可视化 X = dig ...

  6. 零基础学Java之Java学习笔记(一):Java概述

    什么是Java? Java是一门面向对象编程语言,可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序. Java特点有哪些? 1.Java语言吸收了C++语言的各种优点,具有功能强大 ...

  7. 从一次netty 内存泄露问题来看netty对POST请求的解析

    背景 最近生产环境一个基于 netty 的网关服务频繁 full gc 观察内存占用,并把时间维度拉的比较长,可以看到可用内存有明显的下降趋势 出现这种情况,按往常的经验,多半是内存泄露了 问题定位 ...

  8. NOIP 模拟 $32\; \rm Smooth$

    题解 \(by\;zj\varphi\) 很简单的贪心题. 开 \(B\) 个队列,每个队列存最后一次乘上的数为当前队列编号的数. 每次去所有队列中队首的最小值,不用开堆,因为开堆用于将所有数排序,但 ...

  9. docker 镜像配置

    Ubuntu14.04.Debian7Wheezy 对于使用 upstart 的系统而言,编辑 /etc/default/docker 文件,在其中的 DOCKER_OPTS 中配置加速器地址: DO ...

  10. WPF设计自定义控件

    在实际工作中,WPF提供的控件并不能完全满足不同的设计需求.这时,需要我们设计自定义控件. 这里LZ总结一些自己的思路,特性如下: Coupling UITemplate Behaviour Func ...