20210715 noip16
考场
乍一看 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
把上下边界也看作两点。
有两种理解:
- 点集的每个子集都会构成一个屏障,显然每个屏障选择穿过最长的边,答案即为所有最长边的最小值。考虑直接求出最小的屏障,那么从上边界开始,每次把距离当前点集最近的点加入点集,下边界被加入点集时结束。发现这个算法就是 Prim
- 相当于求从上边界到下边界的最小瓶颈路,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])\) 相交:
\]
下文代码是 \(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
把式子化成
\]
那么把每个点看作二维坐标系上 \((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的更多相关文章
- [Luogu 1850] noip16 换教室
[Luogu 1850] noip16 换教室 好久没有更博客了,先唠嗑一会,花了两天的空闲时间大致做完了昨年的noip真题 虽然在经过思考大部分题目都可出解(天天爱跑步除外),但是并不知道考试时候造 ...
- 【NOIP16提高组】换教室
[题目链接] 点击打开链接 [算法] 概率DP 先跑一遍floyed,求出每个教室之间的最短路径,存在数组dist[][]中,时间复杂度O(V^3) 设计状态,f[i][j][k]表示当前选到第i个教 ...
- noip16
<凉宫春日的忧郁>专场 T1 考试的时候连题面都没看懂,都没往图论这方面想,更别提最小生成树. 正解: 最小生成树prim,好像是什么欧几里得生成树,寒假时候的东西了,我直接找的blog看 ...
- 20210716考试-NOIP16
考场时Prim的 $i$ 写成 $k$ 100->0 rank1->rank23 T1 Star Way To Heaven 考场正解:假设你要二分答案,则几个圆组成几道"屏障& ...
- 调用免费API查询全年工作日、周末、法定节假日、节假日调休补班数据
前言 日常开发中,难免会用到判断今天是工作日.周末.法定节假日.节假日调休补班做一些业务处理,例如:仅在上班时间给用户推送消息.本文记录调用免费API查询全年工作日.周末.法定节假日.节假日调休补班数 ...
- Java 中节省 90% 时间的常用的工具类
前言 你们有木有喜欢看代码的领导啊,我的领导就喜欢看我写的代码,有事没事就喜欢跟我探讨怎么写才最好,哈哈哈...挺好. 今天我们就一起来看看可以节省 90% 的加班时间的第三方开源库吧,第一个介绍的必 ...
- Java流程控制01——用户交互Scanner
用户交互Scanner sacnner对象 之前的语法并没有实现程序与人的交互.java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入. 基本语法: S ...
- Python小白的数学建模课-19.网络流优化问题
流在生活中十分常见,例如交通系统中的人流.车流.物流,供水管网中的水流,金融系统中的现金流,网络中的信息流.网络流优化问题是基本的网络优化问题,应用非常广泛. 网络流优化问题最重要的指标是边的成本和容 ...
- centos7系统上pgsql的一些报错解决方法
1.2021-07-15 # 问题: 登录时服务器拒绝连接 psql -h 192.168.1.112 # 解决方法:修改配置文件 pg_hba.conf ,将该主机加进白名单 vi pg_hba.c ...
随机推荐
- 解决iOS上网页滑动不流畅问题
body { overflow:auto; /* 用于 android4+,或其他设备 */ -webkit-overflow-scrolling:touch; /* 用于 ios5+ */ }说明: ...
- 什么是软件的CLI安装
Websoft9 在进行开源软件的集成与自动化安装研究过程中发现有些软件有CLI安装模式,例如Gitlab CLI版本.Ghost CLI.PHP CLI等,CLI安装是什么意思? CLI(Comma ...
- 题解AGC004C
题目 . 样例 AGC 好评. 题意:让你在一个 \(H \times W\) 的方格纸上找两个连通块,使得他们的重合部分就是输入中给的部分. 先放个样例. 输入: 5 5 ..... .#.#. . ...
- dubbo学习实践(3)之Dubbo整合Consul及Dubbo配置方式
前言:上一篇中,已经写到了使用zookeeper为注册中心的配置,下面写下配置Consul为注册中心 1. Consul注册中心验证 修改provider和consumer的服务配置文件 Provid ...
- 【笔记】KNN之分类准确度
分类准确度 分类准确度 以sklearn中的手写数字datasets.load_digits为例,其是8*8的图形,具有64个特征值,类别由0到9 我们挑选出666这个图形,将其可视化 X = dig ...
- 零基础学Java之Java学习笔记(一):Java概述
什么是Java? Java是一门面向对象编程语言,可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序. Java特点有哪些? 1.Java语言吸收了C++语言的各种优点,具有功能强大 ...
- 从一次netty 内存泄露问题来看netty对POST请求的解析
背景 最近生产环境一个基于 netty 的网关服务频繁 full gc 观察内存占用,并把时间维度拉的比较长,可以看到可用内存有明显的下降趋势 出现这种情况,按往常的经验,多半是内存泄露了 问题定位 ...
- NOIP 模拟 $32\; \rm Smooth$
题解 \(by\;zj\varphi\) 很简单的贪心题. 开 \(B\) 个队列,每个队列存最后一次乘上的数为当前队列编号的数. 每次去所有队列中队首的最小值,不用开堆,因为开堆用于将所有数排序,但 ...
- docker 镜像配置
Ubuntu14.04.Debian7Wheezy 对于使用 upstart 的系统而言,编辑 /etc/default/docker 文件,在其中的 DOCKER_OPTS 中配置加速器地址: DO ...
- WPF设计自定义控件
在实际工作中,WPF提供的控件并不能完全满足不同的设计需求.这时,需要我们设计自定义控件. 这里LZ总结一些自己的思路,特性如下: Coupling UITemplate Behaviour Func ...