T1:Star Way To Heaven

基本思路:

  最小生成树。

  假如我们将上边界与下边界看作一个点,然后从上边界经过星星向下边界连边,会发现,他会形成一条线将整个矩形分为左右两个部分。

  并且很明显从左边界走到右边界一定会经过这条线的某一部分。

  为了与星星和上下边界尽可能远,我们直接走两点连边的中点,这应该很好理解。

  他要求最小距离,所以我们可以跑最小生成树。

  然后在最小生成树的边里拣大的输出他的\(1/2\)即可。

  我们将所有星星连边,并与上下边界连边,与上下边界连边的权值是\(y\)以及\(m-y\)。

  然后,我们还面临一个问题,就是我们生成的是树,对于一个完全图来说,这棵树不可能退化成链,就是说,他至少有两支,而我们要的是这棵树上最大权值最小的那一支上的最大值,总不可能求出树后在\(DFS\)吧。

  考虑最小生成树的原理:贪心。

  他每次都会选权值最小加入生成树,那么当代表下界的点第一次被加入生成树时直接return即可,之前得到的那一支就是我们要的,记得在之前不断对边取max即可。

  最后输出时记得除以2.

  代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
const int SIZEK=6006;
int k;
double n,m;
struct Star{double x,y;}star[SIZEK];
bool operator<(Star a_,Star b_)
{
if(a_.x==b_.x) return a_.y<b_.y;
return a_.x<b_.x;
}
double ans=-999999;
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
struct edge{int f,t;double w;}a[SIZEK*SIZEK/2];
bool operator<(const edge a_,const edge b_){return a_.w<b_.w;}
int cnt;
double len(int i,int j)
{
double l;
l=(star[i].x-star[j].x)*(star[i].x-star[j].x);
l+=(star[i].y-star[j].y)*(star[i].y-star[j].y);
l=sqrt(l);
return l;
}
int f[SIZEK];
int find(int x)
{
if(f[x]==x)
return x;
return f[x]=find(f[x]);
}
void join(int x,int y)
{
if(f[x]!=f[y])
f[f[y]]=f[x];
}
};
using namespace STD;
int main()
{
int x=scanf("%lf%lf",&n,&m);
k=read();
for(rr int i=1;i<=k;i++) x=scanf("%lf%lf",&star[i].x,&star[i].y),f[i]=i;
f[k+1]=k+1,f[k+2]=k+2;
//最近对优化连边
sort(star+1,star+1+k);
for(rr int i=1;i<=k;i++)
for(rr int j=i+1;j<=min(k,i+500);j++)
{
a[++cnt].f=i;
a[cnt].t=j;
a[cnt].w=len(i,j);
}
//
for(rr int i=1;i<=k;i++)
{
a[++cnt].f=i;
a[cnt].t=k+1;
a[cnt].w=star[i].y;
a[++cnt].f=i;
a[cnt].t=k+2;
a[cnt].w=m-star[i].y;
}
sort(a+1,a+1+cnt);
for(rr int i=1;i<=cnt;i++)
{
if(find(k+1)==find(k+2)) break;
int f=a[i].f;
int t=a[i].t;
if(find(f)!=find(t))
{
join(f,t);
ans=max(ans,a[i].w);
}
}
printf("%.9lf",ans/2.00);
}

  改题途中:

  正解用的是Prim,因为这样的话就不用把边真正建出来了,但是我更熟悉kruscal。所以用的kruscal。但是MLE了。

  后来战神教我用点对减少连边我才A的。

  这也从一个侧面印证了kruscal在稠密图上的局限性。

T2:God Knows

基本思路:

  数据结构优化DP

  我们可以发现,与边\((i,p[i])\)相交的边\((j,p[j])\)一定满足:

  i<j&&p[i]>p[j] 或 i>j&&p[i]<p[j]

  如果您急于看正解,接下来这一段可以直接跳过

  

  

  先说一个考场上想的稀奇思路:

  我们可以给所有相交的的边的i与j连边,然后问题转化成了以最小的代价将整个图里的边盖。

  为什么这个思路是值得考虑的呢?

  我们可以发现,我们发现,将图中的边删去只需要将与他相交的边删去或将它上的点删去即可。

  这很像将图中边覆盖时至少要将他的一个端点选中。

  所以,这个思路值得考虑。

  但是他在这个题中实际上是不可行的。因为图的边完全覆盖是NP问题。。。。。

  就是下面链接里提到的广播问题。。。。。。。

  不知道啥是NP问题??戳这里

  总之1s内是解决不了了。

  但是假如图退化为树,就可以树规了。

  这个思路还有一个可能的实现就是将建出的图中的点改为边,边改为点,然后跑最小欧拉路。

  至于建边,归并排序的逆序对算法也许可以做到\(O(nlogn)\)实现。

  至于是否真的可以,作者没有精力实现。

  留给读者思考。

  如果您将他证伪或解除,请您告诉我。

  这个思路只是作为一个启发提出,希望也许可以得到一些其他的灵感。

  

  

  然后说正解。

  第一行已经说了,数据结构优化DP。

  我们可以发现,这道题与线性DP中的有好城市问题类似。

  传送门:友好城市

  我们观察可以发现答案的边一定是不相交的,那么他们的端点一定成上升序列。

  因为只有边没有被删去的点才会有贡献。

  所以相交的边的点一定只有其一做出贡献。

  我们可以求出图里所有的极长上升子序列。

  然后取他们c的和的最小值,即可。

  复杂度\(O(n^2)\)。

  考虑优化。

  用线段树维护p的范围,线段树里插入的是i。

  那么我们每次在线段树里查询1->p[i]-1即可。

  还要考虑相交的线都可能对同一点作出贡献。所以我们要循环查询p[j]->p[i]-1,将j更新为查询结果。

  因为我是从小到大插入的i所以查询到的结果一定与之前的j相交或平行,而平行的话也是最大i,所以一定可以贡献。

  上代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
#define inf INT_MAX
const int SIZE=2e5+4;
int n,ans=inf;
int p[SIZE],cost[SIZE];
int c[SIZE];
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
bool vis[SIZE];
class Line_tree
{
private:
#define lc id<<1
#define rc id<<1|1
int maxn[SIZE<<2];
public:
Line_tree(){}
void insert(int id,int l,int r,int pos,int v)
{
// if(id>=(SIZE<<2))
if(l==r){maxn[id]=v;return;}
int mid=(l+r)>>1;
if(pos<=mid) insert(lc,l,mid,pos,v);
else insert(rc,mid+1,r,pos,v);
maxn[id]=max(maxn[lc],maxn[rc]);
}
int query(int id,int l,int r,int st,int en)
{
if(st<=l&&r<=en) return maxn[id];
int mid=(l+r)>>1;
rr int ret=-inf;
if(st<=mid) ret=max(ret,query(lc,l,mid,st,en));
if(mid<en) ret=max(ret,query(rc,mid+1,r,st,en));
return ret;
}
}t;
};
using namespace STD;
int main()
{
n=read();
for(rr int i=1;i<=n;i++) p[i]=read();
for(rr int i=1;i<=n;i++)
{
cost[i]=read();
c[i]=inf;
}
int id=0;
for(rr int i=1;i<=n;i++)
{
id=0;
if(p[i]-1>=1)
id=t.query(1,1,n,1,p[i]-1);
while(id)
{
c[i]=min(c[i],c[id]+cost[i]);
vis[id]=1;
if(p[id]+1>p[i]-1)
break;
id=t.query(1,1,n,p[id]+1,p[i]-1);
}
if(c[i]==inf) c[i]=cost[i];
t.insert(1,1,n,p[i],i);
}
for(rr int i=1;i<=n;i++)
if(!vis[i])
ans=min(ans,c[i]);
printf("%d",ans);
}

  最后在解释下为什么是极大上升序列吧,因为他可以保证平行,并且使得每一组相交线里都会有一条直线被选中

T3:Loost My Music

  斜率优化DP·可持久化栈(主席树)·二分·凸包

  将这个题的式子取反。会得到:

\[-\frac{c_{u}-c_{v}}{depth_{u}-depth_{v}}
\]

假如我们以c为y轴,depth为x轴,会发现,这其实是相邻两点连线的斜率取反。

  因此,我们可以考虑用单调栈维护下凸包。然后将栈顶与当前值计算即可。

  注意当回溯后要继续向下搜,所以要恢复栈状态,因此考虑可持久化栈。

  上代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
const int SIZE=5e5+4;
int n;
int top[SIZE];
int fa[SIZE],to[SIZE];
int dire[SIZE],head[SIZE];
double val[SIZE],depth[SIZE];
double ans[SIZE];
inline void add(int f,int t)
{
static int num1=0;
to[++num1]=t;
dire[num1]=head[f];
head[f]=num1;
}
namespace Prisident_tree
{
struct node
{
int id;
node *l,*r;
}root[SIZE];
void build(node &now,int l,int r)
{
if(l==r) {if(l==1) now.id=1;return;}
rr int mid=(l+r)>>1;
now.l=new node;
now.r=new node;
build(*now.l,l,mid),build(*now.r,mid+1,r);
}
void insert(node &before ,node &now,int l,int r,int pos,int id)
{
if(l==r){now.id=id;return;}
rr int mid=(l+r)>>1;
if(pos<=mid)
{
now.r=before.r;
now.l=new node;
insert(*before.l,*now.l,l,mid,pos,id);
}
else
{
now.l=before.l;
now.r=new node;
insert(*before.r,*now.r,mid+1,r,pos,id);
}
}
int query(rr node now,rr int l,rr int r,rr int pos)
{
if(l==r) return now.id;
rr int mid=(l+r)>>1;
if(pos<=mid)
return query(*now.l,l,mid,pos);
else
return query(*now.r,mid+1,r,pos);
}
};
using namespace Prisident_tree;
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
inline double rate(rr int id1,rr int id2){return (val[id1]-val[id2])/(depth[id1]-depth[id2]);}
int find(int f,int now)
{
int l=1,r=top[f];
rr double a1,a2;
while(l<r)
{
rr int mid=(l+r)>>1;
rr int id1=query(root[f],1,n,mid);
rr int id2=query(root[f],1,n,mid+1);
a1=rate(id2,id1);
a2=rate(now,id2);
if(a1>=a2) r=mid;
else l=mid+1;
}
return l;
}
void dfs(int now)
{
if(depth[now]==1.00)
{
ans[now]=rate(now,1);
insert(root[fa[now]],root[now],1,n,2,now);
top[now]=2;
}
else
{
if(now!=1)
{
int pos=find(fa[now],now);
top[now]=pos+1;
int id=query(root[fa[now]],1,n,pos);
ans[now]=rate(now,id);
insert(root[fa[now]],root[now],1,n,top[now],now);
}
}
for(rr int i=head[now];i;i=dire[i])
{
depth[to[i]]=depth[now]+1.00;
dfs(to[i]);
}
}
};
using namespace STD;
int main()
{
n=read();
for(rr int i=1;i<=n;i++) int x=scanf("%lf",val+i);
for(rr int i=2;i<=n;i++) fa[i]=read(),add(fa[i],i);
build(root[1],1,n);
dfs(1);
for(rr int i=2;i<=n;i++) printf("%.10lf\n",-ans[i]);
}

2021.7.16 现役

NOIP模拟16:「Star Way To Heaven·God Knows·Loost My Music」的更多相关文章

  1. NOIP 模拟 $16\; \rm Star Way To Heaven$

    题解 \(by\;zj\varphi\) 看懂题!!! 从最左穿到最右,一定会经过两个星星之间或星星和边界之间,那么我们穿过时当前最优一定是走中点 而我们要求最小的距离最大,那么我们将所有星星和边界( ...

  2. 7.15考试总结(NOIP模拟16)[Star Way To Heaven·God Knows·Lost My Music]

    败者死于绝望,胜者死于渴望. 前言 一看这个题就来者不善,对于第一题第一眼以为是一个大模拟,没想到是最小生成树. 对于第二题,先是看到了状压可以搞到的 20pts 然后对着暴力一顿猛调后来发现是题面理 ...

  3. 2021.7.15考试总结[NOIP模拟16]

    ZJ模拟D2就是NB.. T1 Star Way To Heaven 谁能想到这竟是个最小生成树呢?(T1挂分100的高人JYF就在我身边 把上边界和下边界看成一个点和星星跑最小生成树,从上边界开始跑 ...

  4. Noip模拟16 2021.7.15

    题目真是越来越变态了 T1 Star Way To Heaven 首先,你要看出这是一个最小生成树的题(妙吧?) 为什么可以呢? 我们发现从两点连线的中点过是最优的,但是上下边界怎么办呢? 我们把上下 ...

  5. NOIP模拟测试「简单的区间·简单的玄学·简单的填数·简单的序列」

    简单的区间 $update$ 终于$AC$了 找到$(sum[r]+sum[l](sum表示以中间点为基准的sum)-mx)\%k==0$的点 注意这里$sum$表示是以$mid$为基准点,(即$su ...

  6. NOIP模拟 16

    嗯我已经是个不折不扣的大辣鸡了 上次的T3就弃了,这次又弃 颓废到天际 T1 巨贪贪心算法 我就是一个只会背板子的大辣鸡 全裸的贪心看不出来,只会打板子 打板子,加特判,然后一无进展,原题不会,这就是 ...

  7. [NOIP模拟16]题解

    A.Blue 出题人大概已经去为国家处理积压子弹了? 贪心,让每一只青蛙(我怂行吧)都尽量往远跳,能到达的最远的被踩了就跳次远的,以此类推.可以维护一个单调队列,表示每只青蛙的位置(开始都是0).然后 ...

  8. [考试总结]noip模拟16

    达成成就,一天更3篇总结. 又是一个暴力场 别问我为什么开局 \(5\) 分钟就问老师为什么 \(T3\) 没有提交的窗口. 开题读题,一路自闭到 \(T3\) ,发现 \(T3\) 可打暴力,所以一 ...

  9. NOIP 模拟 $16\; \rm Lost My Music$

    题解 \(by\;zj\varphi\) 一道凸包的题 设 \(\rm dep_u\) 表示节点 \(u\) 的深度,那么原式就可化为 \(-\frac{c_v-c_u}{dep_v-dep_u}\) ...

随机推荐

  1. rsync(873)未授权访问

    cd vulhub-master/rsync/common docker -composeup -d 检测 1.列出目标服务器的同步目录 rsync 192.168.244.129:: 2.查看模块文 ...

  2. Hyper-V下Internal vSwitch的配置和Linux虚拟机的SSH连接

    最近工作中要在Windows Server 2016/Hyper-V 10中运行Ubuntu16实例,需要制作出"即插即用"的镜像文件,也就是安装好后即可从外部SSH进去.之前我使 ...

  3. 租了一台华为云耀云服务器,却直接被封公网ip,而且是官方的bug导致!

    小弟在博客园也有十多个年头了,因为离开了.net圈子,所以很少发文,今天可算是被华为云气疯了.写下这篇文章,大家也要注意自查一下,避免无端端被封公网ip. 因为小弟创业公司业务发展,需要一个公网做宣传 ...

  4. mysql zip 安装

    第一步下载mysql.zip https://dev.mysql.com/downloads/mysql/5.7.html#downloads 第二步:解压文件后在其目录下, 新建   my.ini ...

  5. Mybatis学习笔记-注解开发

    面向接口编程 根本原因:[解耦],[可拓展],[更高规范性] 接口类型: abstract class interface 使用注解开发 简单语句可用注解开发(直接查询,列名与属性名相同) 本质:反射 ...

  6. Android 键盘属性

    键盘定义:通过Keyboard说明是一个软键盘定义文件,Row元素说明这是一行按键的定义,Key元素说明这是一个按键的定义.Key元素通过一些属性来定义每个按键,下面是一些常用的属性介绍: Codes ...

  7. 跟你说个笑话,硕士毕业两年,月薪10k,天天面向CV编程

    "枯燥乏味的一天,又tm要开始了". 早上10:00,程序员毛毛带着路上买的早餐,打开24英寸的显示屏,去某论坛查一下昨天没有解决的bug. 9 个小时增删改查.搬砖写代码的一天又 ...

  8. 【vue3】封装自定义全局插件

    [vue3]封装自定义全局插件 原vue2方法 main.js import Vue from 'vue' import App from './App.vue' import router from ...

  9. DI 原理解析 并实现一个简易版 DI 容器

    本文基于自身理解进行输出,目的在于交流学习,如有不对,还望各位看官指出. DI DI-Dependency Injection,即"依赖注入":对象之间依赖关系由容器在运行期决定, ...

  10. 关于c#:如何在不同的命名空间中处理相同的类名?

    How to handle same class name in different namespaces? 我正在尝试创建一个通用的库结构. 我通过为我想要的每个公共库创建单独的项目来做到这一点 我 ...