2020-07-05 热身训练赛(五)
A.(Gym-12483A)
B.(Gym-12483B)
C.构造图(Gym-12483C)
题意:给出一个树,要求写出每个节点的坐标,使得相邻节点之间的距离为1,并且任何一对节点之间的距离最少为$10^{-4}$,相邻节点之间的距离与1的绝对误差最多为$10^{-6}$,不连接在同一节点的边之间的距离至少为$10^{-6}$,坐标的绝对值不超过$3000$。
解:分配边转化为分配角度$\theta$,那么点的坐标就是$(x+cos\theta,y+sin\theta)$,为了避免交叉,利用树的结构进行dfs,角度按照dfs的顺序增大,这样就可以保证边不重叠了,实际上不用刻意的研究$10^{-4}和10^{-6}$,可以认为这两个数字就表示无穷小,也就是点重叠,边交叉的情况。比赛的时候没认真考虑边交叉,就用了bfs,后来队友也写了一个,写的dfs,但也wa了。赛后去看了一下别人的代码,才突然意识到这个问题。队友写的dfs我也看了一下,发现里面出了一个极小无比的错误,改掉就过了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#define maxn 1001
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
using namespace std;
int num,head[maxn];
struct node{
int to,pre;
}e[maxn*2];
void Insert(int from,int to){
e[++num].to=to;
e[num].pre=head[from];
head[from]=num;
}
struct point{
double x,y;
}p[maxn];
int n;
double Eps;
bool vis[maxn];
int cnt=-1;
void dfs(int now){
for(int i=head[now];i;i=e[i].pre){
int to=e[i].to;
if(!vis[to]){
vis[to]=1;
cnt++;
p[to].x=p[now].x+cos(Eps*cnt);
p[to].y=p[now].y+sin(Eps*cnt);
dfs(to);
}
}
}
int main(){
scanf("%d",&n);
Eps=(double)M_PI/(double)1000;
int x,y;
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
Insert(x,y);Insert(y,x);
}
p[1].x=0;p[1].y=0;
vis[1]=1;
dfs(1);
for(int i=1;i<=n;i++){
printf("%.15f %.15f\n",(double)p[i].x,(double)p[i].y);
}
return 0;
}
D.(Gym-12483D)
E.(Gym-12483E)
F.(Gym-12483F)
G.构造障碍(Gym-12483G)
题意:在一个平面上有一个小球,许多障碍物和一个洞(洞的坐标为(0,0)),将平板向一个方向倾斜,就可以使小球向这个方一直滚动,直到遇到障碍物或者进洞。题目给出一个包含LRUD的字符串,表示平板倾斜的方向序列,要求根据这个序列设计一种障碍物的摆放方法,使小球最后能顺利进洞,不能满足条件的输出impossible。
解:规定小球从(0,0)开始滚动,最后根据最终的位置应该在(0,0),把所有的点进行平移。可以先判断能够产生impossible的情况,impossible实际上就是中途进洞,因为最后一步移动要把小球移动到洞里,所以如果有LRL,RLR,UDU,DUD这四种情况出现在最后三次操作中,就直接输出impossible了。求路径时,如果有RLRL这类情况,就可以只设置左右两个障碍,如果出现RU这类情况,就把小球向U移两格,设置新的障碍。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define maxn 10010 using namespace std; char s[21]; int n,cnt; bool flag; struct node{ int x,y; bool operator == (const node u)const{ if(x==u.x&&y==u.y)return 1; return 0; } bool operator < (const node u)const{ if(x!=u.x)return x<u.x; return y<u.y; } }ans[maxn]; int main(){ scanf("%s",s+1); n=strlen(s+1); if(n>=3){ if(!strcmp(s+n-2,"LRL"))flag=1; if(!strcmp(s+n-2,"RLR"))flag=1; if(!strcmp(s+n-2,"UDU"))flag=1; if(!strcmp(s+n-2,"DUD"))flag=1; } if(flag){ puts("impossible"); return 0; } int d=1; int x=0,y=0; for(int i=1;i<=n;i++){ if(s[i]=='R'){ x=d; ans[++cnt]={x+1,y}; } if(s[i]=='L'){ x=-d; ans[++cnt]={x-1,y}; } if(s[i]=='U'){ y=d; ans[++cnt]={x,y+1}; } if(s[i]=='D'){ y=-d; ans[++cnt]={x,y-1}; } if(i+1<=n){ if(s[i]=='L'&&s[i+1]=='R'); else if(s[i]=='R'&&s[i+1]=='L'); else if(s[i]=='U'&&s[i+1]=='D'); else if(s[i]=='D'&&s[i+1]=='U'); else d+=2; } } printf("%d %d\n",-x,-y); sort(ans+1,ans+cnt+1); n=unique(ans+1,ans+cnt+1)-ans-1; printf("%d\n",n); for(int i=1;i<=n;i++){ printf("%d %d\n",ans[i].x-x,ans[i].y-y); } return 0; }
H.(Gym-12483H)
题意:一共有n个位置,可以填入0或1,如果相邻两个数字不同,就称为有一个change,题目中指定b个位置只能为0(约定第一个位置不指定,最后一个位置指定),其它位置填0或者1,使得恰好有c个change,要求写出一种符合要求的01串。
解:可以发现如果在两个0之间填1,无论如何都会增加偶数个change,而如果在第一个位置填了1,那么无论如何都会增加奇数个1。所以可以通过c的奇偶性来判断第一个位置应该填的数。然后根据剩下的需要满足的change数,来顺次填不一样的数字。
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500010
using namespace std;
int n,c,b,a[maxn];
int main(){
scanf("%d%d%d",&n,&c,&b);
memset(a,-1,sizeof(a));
int x;
for(int i=1;i<=b;i++){
scanf("%d",&x);
a[x]=0;
}
if(c%2==1){a[1]=1;}
else a[1]=0;
for(int i=2;i<=n;i++){
if(a[i]==-1){
if(c>0)a[i]=!a[i-1];
}
if(a[i]!=a[i-1])c--;
if(c<=0)break;
}
for(int i=1;i<=n;i++){
if(a[i]==-1)a[i]=0;
printf("%d",a[i]);
}
puts("");
return 0;
}
I.(Gym-12483I)
J.(Gym-12483J)
K.(Gym-12483K)