Gym .102021 .German Collegiate Programming Contest (GCPC 18) (寒假gym自训第三场)
B .Battle Royale
题意:给你两个点A,B,以及一个圆S,保证两个点在圆外,且其连线与圆相交,求两点间最短距离。
思路:显然是要分别与圆相切,然后在圆弧想走,直到相交。 那么ans=与圆相交的直线距离+圆弧上的距离; 前者不难求。 后者的话有些抽象,因为不知道怎么取固定角度,但是如果想到atan2了就不难了,因为atan2求出的角度是固定了标准的,注意对用两个atan2求出来的角度求其夹角时,注意不要错过2pi;同时取min(angle,2*pi-angle)。 还不懂的,看代码。
(camp也有类似的题
#include<bits/stdc++.h> using namespace std; const double pi=acos(-1.0); double ans; double dist(double x1,double y1,double x2,double y2){ return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } pair<double,double> angle(double x1,double y1,double x,double y,double r) { double d=dist(x1,y1,x,y); ans+=(sqrt(d*d-r*r)); double ang1=atan2(y1-y,x1-x); double ang2=acos(r/d); return make_pair(ang1+ang2,ang1-ang2); //返回圆心与切点的角度 } double getlen(double A,double B,double r){ A=fabs(A-B); if(A>=pi*2) A-=pi*2; return r*min(A,pi*2-A); } int main() { double x1,y1,x2,y2,x,y,r; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); scanf("%lf%lf%lf",&x,&y,&r); scanf("%lf%lf%lf",&x,&y,&r); pair<double,double> A=angle(x1,y1,x,y,r); pair<double,double> B=angle(x2,y2,x,y,r); double Cirlen=getlen(A.first,B.first,r); Cirlen=min(Cirlen,getlen(A.first,B.second,r)); Cirlen=min(Cirlen,getlen(A.second,B.first,r)); Cirlen=min(Cirlen,getlen(A.second,B.second,r)); printf("%.10lf\n",ans+Cirlen); return 0; }
E .Expired License
题意:给定你两个最多带5位小数的double型a,b,让你用用一对素数x y表示他们的比值。
思路:我们把a和b都乘1e5,化简验证是否是素数即可。 但是要注意精度误差,我们可以给把数加一个1e-7之后再乘1e5.
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(ll i=a;i<=b;i++) using namespace std; const int maxn=10000010; const double eps=1e-7; int p[maxn],cnt; bool vis[maxn]; void prime() { vis[1]=1; rep(i,2,maxn-1){ if(!vis[i]) p[++cnt]=i; rep(j,1,cnt){ if(p[j]*i>=maxn) break; vis[i*p[j]]=1; if(!(i%p[j])) break; } } } int main() { double A,B; int C,D,T; scanf("%d",&T); prime(); while(T--){ scanf("%lf%lf",&A,&B); C=(int)((A+eps)*100000); D=(int)((B+eps)*100000); int g=__gcd(C,D); C/=g; D/=g; if(!vis[C]&&!vis[D]) printf("%d %d\n",C,D); else if(C==1&&D==1) puts("2 2"); else puts("impossible"); } return 0; }
H .Hyper Illuminati
题意:给定一个数N(<1e16),问是否存在n和s,满足1^n+2^n+3^n+...s^n=N;输出n+1,s;
思路:如果2<=n<=53,我们发现s不会太大,我们可以把所有的都求出来;如果n>53,则有s<=2,我们可以把s=2的值都求出来。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(ll i=a;i<=b;i++) using namespace std; int main() { ll ans=0,N; cin>>N; //N=M; rep(i,2,53){ //幂位i+1 ans=0; rep(j,1LL,N){ ll tmp=1; rep(k,1,i){ if(tmp>N/j) {tmp=N+1; break;} tmp*=j; } if(tmp>N) break; ans+=tmp; if(ans==N) return printf("%lld %lld\n",i+1,j),0; if(ans>=N) break; } } rep(i,2,2){ //长度位2 ll tmp=1; rep(j,1LL,N){ tmp*=2; if(j>=2){ if(tmp+1==N) return printf("%lld %lld\n",j+1,2),0; } if(tmp+1>=N) break; } } puts("impossible"); return 0; }
D .Down the Pyramid
题意:金字塔的每一层的位置的值=下一层的两个值之和,现在给你最倒数第二层的值a1,a2...an,问你最后一层有多少种情况,满足所哟数不小于0。
思路:我们假设第一个位置位b0=x,那么b1=a1-x; b2=a2-b1=a2-a1+x.. 中间给限制让所有数大于等于0,则有x<=a1; x>=a1-a2; x<=a3+a2-a1 ; x>=...。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; ll a[maxn],sum,Mn,Mx; int main() { int N; scanf("%d",&N); rep(i,1,N) scanf("%lld",&a[i]); Mn=0; Mx=a[1]; rep(i,1,N){ sum=a[i]-sum; if(i&1) Mx=min(Mx,sum); else Mn=max(Mn,-sum); } if(Mx<Mn) puts("0"); else printf("%lld\n",Mx-Mn+1); return 0; }
C .Coolest Ski Route
题意:给定带边权有向图,让你找一个最长的链,满足买个点最多遍历一次。
思路:记忆化搜索即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(ll i=a;i<=b;i++) using namespace std; const int maxn=100010; int dis[maxn],Laxt[maxn],Next[maxn],To[maxn],Len[maxn],cnt; void add(int u,int v,int L){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; Len[cnt]=L; } int dfs(int u){ if(dis[u]!=-1) return dis[u]; dis[u]=0; for(int i=Laxt[u];i;i=Next[i]) dis[u]=max(dis[u],dfs(To[i])+Len[i]); return dis[u]; } int main() { int N,M,u,v,l,ans=0; scanf("%d%d",&N,&M); rep(i,1,N) dis[i]=-1; rep(i,1,M){ scanf("%d%d%d",&u,&v,&l); add(u,v,l); } rep(i,1,N) ans=max(ans,dfs(i)); printf("%d\n",ans); return 0; }
F .Fighting Monsters
题意:给定N个怪兽,问是否能选处两个怪兽,使得他们相互攻击,最后活着的怪兽剩1滴血。
思路:即是问是否寻在相邻的Fibonacci数。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; int F[maxn+10],a[maxn+10],Laxt[maxn+10]; int main() { int N,tot=0; F[0]=1; F[1]=1; for(int i=2;;i++){ F[i]=F[i-1]+F[i-2]; if(F[i]>maxn){ tot= i; break;} } scanf("%d",&N); rep(i,1,N) scanf("%d",&a[i]),Laxt[a[i]]=i; rep(i,1,N) { if(a[i]==1&&Laxt[1]>i) return printf("%d %d\n",i,Laxt[1]),0; } rep(i,1,tot-1) { if(Laxt[F[i]]&&Laxt[F[i+1]]) return printf("%d %d\n",Laxt[F[i]],Laxt[F[i+1]]),0; } puts("impossible"); return 0; }
I .It's Time for a Montage
by 罗
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=998244353; const int maxn=1e3+50; const ll inf=0x3f3f3f3f3f3f3f3fLL; int h[maxn]; int v[maxn]; int isok(int x,int n){ for(int i=1;i<=n;i++){ if(h[i]-v[i]+x==0)continue; else if(h[i]-v[i]+x>0)return true; else return false; } return true; } int main() { int n; cin>>n; for(int i=1;i<=n;i++)cin>>h[i]; for(int i=1;i<=n;i++)cin>>v[i]; int ans=0; for(int i=0;i<=1000;i++){ if(isok(i,n)){ cout<<i<<endl; return 0; } } return 0; }
L. Logic Puzzle
题意:就是扫雷,让你复原雷图。
思路:从左上到右下,关系是唯一对应的。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=110; int a[maxn][maxn],vis[maxn][maxn]; int x[9]={1,1,1,-1,-1,-1,0,0,0}; int y[9]={1,-1,0,1,-1,0,1,-1,0}; int main() { int N,M; scanf("%d%d",&N,&M); rep(i,0,N+1) rep(j,0,M+1) scanf("%d",&a[i][j]); rep(i,0,N) rep(j,0,M){ if(a[i][j]==1){ vis[i+1][j+1]=1; rep(k,0,8) { if(i+1+x[k]>=0&&i+1+x[k]<=N+1&&j+1+y[k]>=0&&j+1+y[k]<=M+1) a[i+1+x[k]][j+1+y[k]]--; } } else vis[i+1][j+1]=-1; } rep(i,0,N+1) rep(j,0,M+1) { if(a[i][j]!=0) { return puts("impossible"),0;} } rep(i,1,N){ rep(j,1,M) putchar(vis[i][j]==1?'X':'.'); puts(""); } return 0; }
K .Kitchen Cable Chaos
题意:...
思路:就是让你选几个棍子,使得(长度和+K-10)/个数不超过5,而且最大,背包即可。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define rep2(i,w,v) for(int i=w;i>=v;i--) using namespace std; const int maxn=10010; int dp[maxn][61],a[maxn]; int main() { int N,g; scanf("%d%d",&N,&g); rep(i,1,N) scanf("%d",&a[i]); dp[0][0]=1; rep(i,1,N) rep2(j,N,1) rep2(k,maxn-1,a[i]) dp[k][j]|=dp[k-a[i]][j-1]; double ans=-1.0; rep(k,g-10,maxn-1){ rep(j,1,N){ if(!dp[k][j]) continue; double tmp=(k+10-g)*1.0/(j+1.0); if(tmp<=5.0) ans=max(ans,tmp); } } if(ans<0.0) puts("impossible"); else printf("%.8f\n", ans); return 0; } /* 3 70 20 35 50 */
M .Mountaineers
题意:给定N*M座山,Q次询问,让你最小化从山S到山T经过的山的最高高度。
思路:不难想到排序,然后启发式合并。 每次合并两个连通块,并且看有没有询问的S和T分别再两个连通块里。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; int h[maxn],Laxt[maxn],Next[maxn],To[maxn],id[maxn]; int cnt,ans[maxn],fa[maxn],N,M,Q,vis[maxn],sz[maxn]; pair<int,int>p[maxn]; set<int>s[maxn]; set<int>::iterator it; int get(int x,int y){ return (x-1)*M+y; } void add(int u,int v,int op) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; id[cnt]=op; } int find(int x){ return fa[x]; } void merge(int u,int v,int H) { if(sz[u]<sz[v]) swap(u,v); for(it=s[v].begin();it!=s[v].end();it++){ int x=*it; for(int i=Laxt[x],f=-1;i;i=Next[i]) { if(!ans[id[i]]&&s[u].find(To[i])!=s[u].end()) { ans[id[i]]=H; } if(ans[id[i]]){ if(f==-1) Laxt[x]=Next[i]; else Next[f]=Next[i]; } f=i; } } for(it=s[v].begin();it!=s[v].end();it++){ s[u].insert(*it); fa[*it]=u; } s[v].clear(); sz[u]+=sz[v]; } int main() { scanf("%d%d%d",&N,&M,&Q); rep(i,1,N) rep(j,1,M){ cnt++; fa[cnt]=cnt; scanf("%d",&h[cnt]); p[cnt].first=h[cnt],p[cnt].second=cnt; s[cnt].insert(cnt); sz[cnt]=1; } cnt=0; int x1,y1,x2,y2; rep(i,1,Q){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); if(x1==x2&&y1==y2) ans[i]=h[get(x1,y2)]; x1=get(x1,y1); x2=get(x2,y2); add(x1,x2,i); add(x2,x1,i); } sort(p+1,p+N*M+1); rep(i,1,N*M){ int pos=p[i].second,H=p[i].first; vis[p[i].second]=1; if(pos%M!=1&&vis[pos-1]){ int fu=find(pos),fv=find(pos-1); if(fu!=fv) merge(fu,fv,H); } if(pos>M&&vis[pos-M]){ int fu=find(pos),fv=find(pos-M); if(fu!=fv) merge(fu,fv,H); } if(pos<=N*M-M&&vis[pos+M]){ int fu=find(pos),fv=find(pos+M); if(fu!=fv) merge(fu,fv,H); } if(pos%M!=0&&vis[pos+1]){ int fu=find(pos),fv=find(pos+1); if(fu!=fv) merge(fu,fv,H); } } rep(i,1,Q) printf("%d\n",ans[i]); return 0; }
更美妙的方法:依然是建立连通关系,不过我们是建立一棵树,从小到大连通,每次把之前的连通块的根作为当前点的儿子。 最后就可以在线询问了,询问S和T的答案是LCA的点的值。 感觉有点向ST表求LCA的原理。 深度越低,最大值越大,我们我们需要求LCA。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=300010; int fa[maxn],cnt,ans[maxn],vis[510][510],rt; int f[maxn][20],dep[maxn]; vector<int>G[maxn]; struct in{ int x,y,id,h; in(){} in(int xx,int yy,int ii,int hh):x(xx),y(yy),id(ii),h(hh){} friend bool operator <(in w,in v){ return w.h<v.h; } }s[maxn]; void add(int u,int v){ G[u].push_back(v); } int find(int x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]); } void dfs(int u,int pre){ f[u][0]=pre; dep[u]=dep[pre]+1; for(int i=0;i<G[u].size();i++) dfs(G[u][i],u); } int LCA(int u,int v) { if(dep[u]<dep[v]) swap(u,v); for(int i=16;i>=0;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i]; if(u==v) return u; for(int i=16;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; return f[u][0]; } int main() { int N,M,Q,h,x1,x2,y1,y2; scanf("%d%d%d",&N,&M,&Q); rep(i,1,N) rep(j,1,M){ scanf("%d",&h); cnt++; fa[cnt]=cnt; ans[cnt]=h; s[cnt]=in(i,j,cnt,h); } sort(s+1,s+cnt+1); rep(i,1,cnt){ x1=s[i].x; y1=s[i].y; if(vis[x1-1][y1]&&x1!=1&&find(s[i].id)!=find(s[i].id-M)) add(fa[s[i].id],find(s[i].id-M)),fa[find(s[i].id-M)]=fa[s[i].id]; if(vis[x1+1][y1]&&x1!=N&&find(s[i].id)!=find(s[i].id+M)) add(fa[s[i].id],find(s[i].id+M)),fa[find(s[i].id+M)]=fa[s[i].id]; if(vis[x1][y1-1]&&y1!=1&&find(s[i].id)!=find(s[i].id-1)) add(fa[s[i].id],find(s[i].id-1)),fa[find(s[i].id-1)]=fa[s[i].id]; if(vis[x1][y1+1]&&y1!=M&&find(s[i].id)!=find(s[i].id+1)) add(fa[s[i].id],find(s[i].id+1)),fa[find(s[i].id+1)]=fa[s[i].id]; vis[s[i].x][s[i].y]=1; } rt=find(1); dfs(rt,0); rep(i,1,16) rep(j,1,cnt) f[j][i]=f[f[j][i-1]][i-1]; rep(i,1,Q){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); x1=(x1-1)*M+y1; x2=(x2-1)*M+y2; int Lca=LCA(x1,x2); printf("%d\n",ans[Lca]); } return 0; }