吐槽一句:话说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算法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 ",mid); return 0; }
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 ",mid); return 0; }