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*) A-=pi*;
return r*min(A,pi*-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 ;
}

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=;
const double eps=1e-;
int p[maxn],cnt; bool vis[maxn];
void prime()
{
vis[]=;
rep(i,,maxn-){
if(!vis[i]) p[++cnt]=i;
rep(j,,cnt){
if(p[j]*i>=maxn) break;
vis[i*p[j]]=;
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)*); D=(int)((B+eps)*);
int g=__gcd(C,D);
C/=g; D/=g;
if(!vis[C]&&!vis[D]) printf("%d %d\n",C,D);
else if(C==&&D==) puts("2 2");
else puts("impossible");
}
return ;
}

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=,N;
cin>>N; //N=M;
rep(i,,){ //幂位i+1
ans=;
rep(j,1LL,N){
ll tmp=;
rep(k,,i){
if(tmp>N/j) {tmp=N+; break;}
tmp*=j;
}
if(tmp>N) break;
ans+=tmp;
if(ans==N) return printf("%lld %lld\n",i+,j),;
if(ans>=N) break;
}
}
rep(i,,){ //长度位2
ll tmp=;
rep(j,1LL,N){
tmp*=;
if(j>=){
if(tmp+==N) return printf("%lld %lld\n",j+,),;
}
if(tmp+>=N) break;
}
}
puts("impossible");
return ;
}

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=;
ll a[maxn],sum,Mn,Mx;
int main()
{
int N; scanf("%d",&N);
rep(i,,N) scanf("%lld",&a[i]);
Mn=; Mx=a[];
rep(i,,N){
sum=a[i]-sum;
if(i&) Mx=min(Mx,sum);
else Mn=max(Mn,-sum);
}
if(Mx<Mn) puts("");
else printf("%lld\n",Mx-Mn+);
return ;
}

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=;
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]!=-) return dis[u]; dis[u]=;
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=;
scanf("%d%d",&N,&M);
rep(i,,N) dis[i]=-;
rep(i,,M){
scanf("%d%d%d",&u,&v,&l);
add(u,v,l);
}
rep(i,,N) ans=max(ans,dfs(i));
printf("%d\n",ans);
return ;
}

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=;
int F[maxn+],a[maxn+],Laxt[maxn+];
int main()
{
int N,tot=;
F[]=; F[]=;
for(int i=;;i++){
F[i]=F[i-]+F[i-];
if(F[i]>maxn){ tot= i; break;}
}
scanf("%d",&N);
rep(i,,N) scanf("%d",&a[i]),Laxt[a[i]]=i;
rep(i,,N) {
if(a[i]==&&Laxt[]>i) return printf("%d %d\n",i,Laxt[]),;
}
rep(i,,tot-) {
if(Laxt[F[i]]&&Laxt[F[i+]]) return printf("%d %d\n",Laxt[F[i]],Laxt[F[i+]]),;
}
puts("impossible");
return ;
}

I .It's Time for a Montage

by 罗

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=;
const int maxn=1e3+;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int h[maxn];
int v[maxn];
int isok(int x,int n){
for(int i=;i<=n;i++){
if(h[i]-v[i]+x==)continue;
else if(h[i]-v[i]+x>)return true;
else return false;
}
return true;
}
int main()
{
int n;
cin>>n;
for(int i=;i<=n;i++)cin>>h[i];
for(int i=;i<=n;i++)cin>>v[i];
int ans=;
for(int i=;i<=;i++){
if(isok(i,n)){
cout<<i<<endl;
return ;
}
}
return ;
}

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=;
int a[maxn][maxn],vis[maxn][maxn];
int x[]={,,,-,-,-,,,};
int y[]={,-,,,-,,,-,};
int main()
{
int N,M; scanf("%d%d",&N,&M);
rep(i,,N+) rep(j,,M+) scanf("%d",&a[i][j]);
rep(i,,N)
rep(j,,M){
if(a[i][j]==){
vis[i+][j+]=;
rep(k,,) {
if(i++x[k]>=&&i++x[k]<=N+&&j++y[k]>=&&j++y[k]<=M+)
a[i++x[k]][j++y[k]]--;
}
}
else vis[i+][j+]=-;
}
rep(i,,N+) rep(j,,M+) {
if(a[i][j]!=) { return puts("impossible"),;}
}
rep(i,,N){
rep(j,,M) putchar(vis[i][j]==?'X':'.');
puts("");
}
return ;
}

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=;
int dp[maxn][],a[maxn];
int main() {
int N,g;
scanf("%d%d",&N,&g);
rep(i,,N) scanf("%d",&a[i]);
dp[][]=;
rep(i,,N)
rep2(j,N,)
rep2(k,maxn-,a[i])
dp[k][j]|=dp[k-a[i]][j-];
double ans=-1.0;
rep(k,g-,maxn-){
rep(j,,N){
if(!dp[k][j]) continue;
double tmp=(k+-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 ;
}
/*
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=;
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-)*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=-;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==-) 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,,N) rep(j,,M){
cnt++; fa[cnt]=cnt; scanf("%d",&h[cnt]);
p[cnt].first=h[cnt],p[cnt].second=cnt;
s[cnt].insert(cnt); sz[cnt]=;
}
cnt=; int x1,y1,x2,y2;
rep(i,,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+,p+N*M+);
rep(i,,N*M){
int pos=p[i].second,H=p[i].first;
vis[p[i].second]=;
if(pos%M!=&&vis[pos-]){
int fu=find(pos),fv=find(pos-);
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!=&&vis[pos+]){
int fu=find(pos),fv=find(pos+);
if(fu!=fv) merge(fu,fv,H);
}
}
rep(i,,Q) printf("%d\n",ans[i]);
return ;
}

更美妙的方法:依然是建立连通关系,不过我们是建立一棵树,从小到大连通,每次把之前的连通块的根作为当前点的儿子。 最后就可以在线询问了,询问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=;
int fa[maxn],cnt,ans[maxn],vis[][],rt;
int f[maxn][],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][]=pre; dep[u]=dep[pre]+;
for(int i=;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=;i>=;i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
if(u==v) return u;
for(int i=;i>=;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][];
}
int main()
{
int N,M,Q,h,x1,x2,y1,y2;
scanf("%d%d%d",&N,&M,&Q);
rep(i,,N)
rep(j,,M){
scanf("%d",&h); cnt++; fa[cnt]=cnt;
ans[cnt]=h; s[cnt]=in(i,j,cnt,h);
}
sort(s+,s+cnt+);
rep(i,,cnt){
x1=s[i].x; y1=s[i].y;
if(vis[x1-][y1]&&x1!=&&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]&&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-]&&y1!=&&find(s[i].id)!=find(s[i].id-))
add(fa[s[i].id],find(s[i].id-)),fa[find(s[i].id-)]=fa[s[i].id];
if(vis[x1][y1+]&&y1!=M&&find(s[i].id)!=find(s[i].id+))
add(fa[s[i].id],find(s[i].id+)),fa[find(s[i].id+)]=fa[s[i].id];
vis[s[i].x][s[i].y]=;
}
rt=find();
dfs(rt,);
rep(i,,)
rep(j,,cnt) f[j][i]=f[f[j][i-]][i-];
rep(i,,Q){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1=(x1-)*M+y1; x2=(x2-)*M+y2;
int Lca=LCA(x1,x2);
printf("%d\n",ans[Lca]);
}
return ;
}
上一篇:2018 ACM-ICPC, Syrian Collegiate Programming Contest


下一篇:asp.net core系列 50 Identity 授权(中)