0817NOIP模拟测试赛后总结

吐槽一句:话说NOIP都取消了还叫NOIP模拟真的好么

于是乎我再次爆炸……(0+20+50=70 rank26)


赛时状态

赛时的状态依旧不佳。不过还是硬逼着自己把三道题都读完,然后开始对出题人静坐示威。

突然意识到这是昨天那套题的day2。后悔昨天没机灵点颓一下。好吧也颓不到。毕竟昨天那套题的题解网上都找不到。

偷瞥了一眼机房其他人的状态,似乎都非常头疼的样子。一向嘈杂的机房甚至没有几声敲键盘键盘声。

嗯,我放心了。

继续抱头想T1,看着double的数据突然想到了二分答案。/滑稽

然后就没有然后了。想了一个多小时无果,决定弃坑。

码了一个T2的20分暴力,T3的20分暴力,然后开始等死。

(当时并没有意识到T3的随机数据还可以送20分,还有意外拿的水数据10分)

抱头想了半天没有一点思路。想二分不知不觉想到了模拟上,我最近真是打模拟打傻了……

然后T2xjb写了个骗分算法。

最后十分钟对拍一下表现我不屈的灵魂!

不到5分钟码出来随机数据生成器和对拍程序结果不知道哪里写挂了,到最后也没调出来。

然后以为自己代码写挂了,心态瞬间崩溃。

出成绩之后不敢看排行榜。闭着眼把进度条拉到最下面,偷偷睁眼看,呀没有我。

一名一名往上翻,不是,不是,不是,woc我是不是没交啊。

70分rank26。好吧,虽然炸了,但T3的50分确实是意外之喜。

赛题题解

T1:Star Way To Heaven

二分答案确实是正解之一。不过不应该照着模拟去想。

T80算法1:

二分距离最小值,给每一对距离小于这个最小值的点建边。

上边界设为0号点,下边界设为k+1号点,从上边界开始跑一趟dfs,如果能搜到k+1,则返回false,否则返回true

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
#define rint register int
using namespace std;
int n,m,k,tot,first[60003];
bool vis[60003];
struct node{int x,y;}p[60003];
struct node2{int u,v,nxt;}edge[600003<<1];
inline double dist(int uu,int vv)
{
    return (p[uu].x-p[vv].x)*(p[uu].x-p[vv].x)+
           (p[uu].y-p[vv].y)*(p[uu].y-p[vv].y);
}
inline void add(int uu,int vv)
{
    edge[++tot]=(node2){uu,vv,first[uu]};
    first[uu]=tot;
}
inline void dfs(int x)
{
    vis[x]=1;
    for(rint i=first[x];i;i=edge[i].nxt)
    {
        int y=edge[i].v;
        if(!vis[y])dfs(y);
    }
    return ;
}
inline bool check(double x)
{
    tot=0;memset(first,0,sizeof(first));
    for(rint i=1;i<k;++i)
        for(rint j=i+1;j<=k;++j)
            if(dist(i,j)<4*x*x)
                add(i,j),add(j,i);
    for(rint i=1;i<=k;++i)
    {
        if(p[i].y<2*x)add(0,i),add(i,0);
        if(p[i].y+2*x>m)add(i,k+1),add(k+1,i);
    }
    memset(vis,0,sizeof(vis));
    dfs(0);
    return !vis[k+1];
}
int main()
{
    scanf("%d %d %d",&n,&m,&k);
    for(rint i=1;i<=k;++i)
        scanf("%d %d",&p[i].x,&p[i].y);
    double l=1,r=1e6;
    while(r-l>1e-11)
    {
        double mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.8lf",l);
    return 0;
}
T80算法1(其实是w50,请读者自行手开long long/懒.jpg)

T80算法2:

依旧是二分,check操作改为并查集实现。原理大致相同。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
#define int long long
#define rint register int
using namespace std;
int n,m,k,tot,first[60003],fa[6003];
bool vis[60003];
struct node{int x,y;}p[60003];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline double dist(int uu,int vv)
{
    return (p[uu].x-p[vv].x)*(p[uu].x-p[vv].x)+(p[uu].y-p[vv].y)*(p[uu].y-p[vv].y);
}
inline bool check(double x)
{
    for(rint i=0;i<=k+1;++i)fa[i]=i;
    for(rint i=1;i<k;++i)
        for(rint j=i+1;j<=k;++j)
            if(dist(i,j)<4*x*x)
            {
                int lx=find(i),ly=find(j);
                if(lx!=ly)fa[lx]=ly;
            }
    for(rint i=1;i<=k;++i)
    {
        if(p[i].y<2*x)
        {
            int lx=find(0),ly=find(i);
            if(lx!=ly)fa[lx]=ly;
        }
        if(p[i].y+2*x>m)
        {
            int lx=find(k+1),ly=find(i);
            if(lx!=ly)fa[lx]=ly;
        }
    }
    return find(0)!=find(k+1);
}
signed main()
{
    scanf("%lld %lld %lld",&n,&m,&k);
    for(rint i=1;i<=k;++i)
        scanf("%lld %lld",&p[i].x,&p[i].y);
    double l=1,r=1e6,mid;
    while(r-l>1e-11)
    {
        mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.8lf\n",mid);
    return 0;
}
T80算法2

AC算法:

考虑如何对T80算法2进行优化。

算法2的主要瓶颈在于并查集的合并中需要$(n^2)$搜一边,这样效率无疑是很低的。

根据并查集的特点,我们不妨预处理出每一个点左上、左下、右上、右下四个方向上最近的点。

不难发现,其他点若也能连接到该点,则一定能通过这四个点中的某一个连接到它。

于是我们每次对每一个点进行合并时只需要扫这四个点就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
#define int long long
#define rint register int
using namespace std;
int n,m,k,tot,first[60003],fa[6003];
int yh[6003][4];
bool vis[60003];
struct node{int x,y;}p[60003];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline double dist(int uu,int vv)
{
    return (p[uu].x-p[vv].x)*(p[uu].x-p[vv].x)+(p[uu].y-p[vv].y)*(p[uu].y-p[vv].y);
}
inline bool check(double x)
{
    for(rint i=0;i<=k+1;++i)fa[i]=i;
    for(rint i=1;i<=k;++i)
        for(rint j=0;j<=3;++j)
        {
            if(!yh[i][j])continue;
            if(dist(i,yh[i][j])<4*x*x)
            {
                int lx=find(i),ly=find(yh[i][j]);
                if(lx!=ly)fa[lx]=ly;
            }
        }
            
    for(rint i=1;i<=k;++i)
    {
        if(p[i].y<2*x)
        {
            int lx=find(0),ly=find(i);
            if(lx!=ly)fa[lx]=ly;
        }
        if(p[i].y+2*x>m)
        {
            int lx=find(k+1),ly=find(i);
            if(lx!=ly)fa[lx]=ly;
        }
    }
    return find(0)!=find(k+1);
}
signed main()
{
    scanf("%lld %lld %lld",&n,&m,&k);
    for(rint i=1;i<=k;++i)
        scanf("%lld %lld",&p[i].x,&p[i].y);
    for(rint i=1;i<=k;++i)
        for(rint j=1;j<=k;++j)
        {
            if(i==j)continue;
            int dis=dist(i,j);
            if(p[j].x<=p[i].x&&p[j].y<=p[i].y)
                yh[i][0]=(dis<dist(yh[i][0],i))?j:yh[i][0];
            if(p[j].x>=p[i].x&&p[j].y<=p[i].y)
                yh[i][1]=(dis<dist(yh[i][1],i))?j:yh[i][1];
            if(p[j].x>=p[i].x&&p[j].y>=p[i].y)
                yh[i][2]=(dis<dist(yh[i][2],i))?j:yh[i][2];
            if(p[j].x<=p[i].x&&p[j].y>=p[i].y)
                yh[i][3]=(dis<dist(yh[i][3],i))?j:yh[i][3];
        }
    double l=1,r=1e6,mid;
    while(r-l>1e-11)
    {
        mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.8lf\n",mid);
    return 0;
}
Accepted 100 930 ms 604 KiB
posted @ 2019-08-17 16:21  hzoi_Joe  阅读(242)  评论(0编辑  收藏  举报