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. 抽奖动画 - lao虎机抽奖

    本文介绍一个lao虎机抽奖动画的实现,lao虎机抽奖在各类商家营销活动中非常常见,这里主要介绍动画的实现过程,其他细节不做详细分析. ps:lao虎机是敏感词,博客园不允许出现,所有老用拼音. 1. ...

  2. CF877B Nikita and string TJ

    前言的前言 本 TJ 同步发布于洛谷,在线求赞(bushi 前言 蒟蒻第一篇题解,在线求审核大大给过 awa. 如果此题解有什么问题的话欢迎各位大巨佬提出. 题目链接:CF877B 题目类型:dp,一 ...

  3. JUC学习笔记(三)

    JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...

  4. CSS实现隐藏滚动条并可以滚动内容

    方法一: 计算滚动条宽度并隐藏起来,其实我只是把滚动条通过定位把它隐藏了起来,下面给一个简化版的代码: <div class="outer-container"> &l ...

  5. springboot上传文件路径存放

    @Beanpublic EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() { return (Config ...

  6. Notes about multiboot usb creator

    U盘上的多系统启动工具,Windows上YUMI比较好,Ubuntu上MultiSystem用法复杂,unetbootin是另外一款,需要安装p7zip(apt-get install p7zip-f ...

  7. Python3实现Two-Pass算法检测区域连通性

    技术背景 连通性检测是图论中常常遇到的一个问题,我们可以用五子棋的思路来理解这个问题五子棋中,横.竖.斜相邻的两个棋子,被认为是相连接的,而一样的道理,在一个二维的图中,只要在横.竖.斜三个方向中的一 ...

  8. MySQL 条件查询

    查询条件 having having的功能和where一样,都是用来筛选查询,不同的是,where在分组之前用,having必须在分组之后使用. # 查询每个部门大于30岁的平均工资,并且保留平均工资 ...

  9. deepin下zerotier组网

    环境:deepin 1.安装zerotier curl -s https://install.zerotier.com/ | sudo bash 官网也是有的 安装完成之后进行内网连接 sudo ze ...

  10. 题解 party?

    传送门 挺遗憾的一个题 考场上想到的思路是题解的退化版,可以有71pts(赛时以为只有20pts),但因为这一场的策略原因没有打-- 首先发现颜色种类数很少,可以直接bitset上树剖维护,炸不了空间 ...