考场

乍一看 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. GoldenEye-v1靶机

    仅供个人娱乐 靶机信息 下载地址:https://pan.baidu.com/s/1dzs_qx-YwYHk-vanbUeIxQ 一.主机扫描 二.信息收集 三.漏洞的查找和利用 boris    I ...

  2. Apache httpd的web服务

    Apache httpd的web服务 适用于Unix/Linux下的web服务器软件 Apache httpd(开源且免费),虚拟主机,支持HTTPS协议,支持用户认证,支持单个目录的访问控制,支持U ...

  3. Android开发如何准备技术面试(含Android面试押题)

    今年毋庸置疑是找工作的寒冬,每一个出来找工作的同学都是值得尊敬的.现在找工作,虽然略难,但是反过来看也会逼迫我们成为更加优秀的自己. 但是不管是旺季还是寒冬,有一些优秀的同学找工作还是挺顺利的.所以说 ...

  4. IDEA Maven快速创建JavaWeb项目

    鉴于这是基本功,而且发现自己经常犯类似的错误,因此详细记录一下这个问题. 1.准备 以笔者的测试软件以及版本为准 IDEA 2020.3 Maven3.6.5 Tomcat 8.5 JDK1.8 2. ...

  5. Java工具类-输入输出流

    输入输出流 1.概念 输入输出流:文件复制,上传 输出流: System.out.println() 写操作,程序将字符流写入到"目的地",比如打印机和文件等 输入流 :Scann ...

  6. LDAP未授权访问学习

    LDAP未授权访问学习 一.LDAP 介绍 LDAP的全称为Lightweight Directory Access Protocol(轻量级目录访问协议), 基于X.500标准, 支持 TCP/IP ...

  7. IDEA spring boot项目插件打包方式jar

    一.打包 1.pom.xml中添加插件依赖 <build> <plugins> <plugin> <!--打包成可执行jar--> <groupI ...

  8. Windows内核开发-6-内核机制 Kernel Mechanisms

    Windows内核开发-6-内核机制 Kernel Mechanisms 一部分Windows的内核机制对于驱动开发很有帮助,还有一部分对于内核理解和调试也很有帮助. Interrupt Reques ...

  9. SpringBoot Spring Security 核心组件 认证流程 用户权限信息获取详细讲解

    前言 Spring Security 是一个安全框架, 可以简单地认为 Spring Security 是放在用户和 Spring 应用之间的一个安全屏障, 每一个 web 请求都先要经过 Sprin ...

  10. ubunt中,使用命令su命令切换root账户,提示认证失败

    报错截图: 解决方法: sudo passwd 重新设置root账户的密码,确认root账户的密码(再次输入密码),然后su ,输入root账户刚刚设置的密码即可切入到root账户: