2018 German Collegiate Programming Contest (GCPC 18)
A、给你一个迷宫,和一个路径,你需要求出路径一共走过的距离,路径是以二维坐标的形式给出的
除了读入实在司马以外,这题就是一个裸的lca求树上路径
不需要离线,在线的倍增也能过
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e6+1e5;
const int mv[4][2]={0,1,0,-1,1,0,-1,0};
int n,m,dep[MAXN],lg[MAXN],fa[MAXN][32],x[MAXN],y[MAXN],vis[MAXN],q;
vector<int> e[MAXN];
char s[1055][2055];
void dfs(int now,int pa)
{
vis[now]=1;
dep[now]=dep[pa]+1;
fa[now][0]=pa;
for(int i=1;(1<<i)<=dep[now];i++)
fa[now][i]=fa[fa[now][i-1]][i-1];
for(auto to:e[now])
if(to!=pa&&!vis[to]) dfs(to,now);
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
if(x==y) return x;
for(int i=lg[dep[x]]-1;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
inline int get(int x,int y)
{
return (x-1)*m+y-1;
}
inline ll dis(int x,int y)
{
int f=lca(x,y);
return dep[x]+dep[y]-2*dep[f];
}
int main()
{
for(int i=1;i<MAXN;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
scanf("%d%d ",&n,&m);
for(int i=0;i<=n;i++)
scanf("%[^\n]%*c",s[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=2*m;j++)
{
if(j&1) continue;
for(int k=0;k<4;k++)
{
int dx=i+mv[k][0],dy=j+mv[k][1];
if(dx<1||dx>n||dy<=1||dy>2*m) continue;
if(k==0||k==1)
if(s[dx][dy]=='|') continue;
if(k==2&&s[i][j]=='_') continue;
if(k==3&&s[dx][dy]=='_') continue;
int x,y,tx,ty;
x=i,tx=dx;
y=j/2,ty=(j+2*mv[k][1])/2;
e[get(x,y)].push_back(get(tx,ty));
e[get(tx,ty)].push_back(get(x,y));
}
}
scanf("%d",&q);
for(int i=0;i<q;i++)
scanf("%d%d",x+i,y+i);
dfs(get(x[0],y[0]),get(x[0],y[0]));
ll ans=0;
for(int i=1;i<q;i++)
ans+=dis(get(x[i],y[i]),get(x[i-1],y[i-1]));
printf("%lld",ans);
return 0;
}
B、给你一个圆和两个点,保证两个点构成的直线与圆有两个交点,问你两点间不经过该圆(但是可以在圆上走)的最短路径
实际上这题给了两个圆,要求你必须在另外一个圆内活动,但是又告诉你另外一个圆一定包含这两个点和这个圆,所以没用
画图看看就发现实际上是两个点分别向圆做切线,找到切点,然后走过两个切点的短弧即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const db eps=1e-10;
const db pi=3.14159265358979;
bool eql(db a,db b){return fabs(a-b)<eps;}
db len(db a,db b,db c,db d){return sqrt((a-c)*(a-c)+(b-d)*(b-d));}
db dot(db a,db b,db c,db d){return a*c+b*d;}
db xc,yc,xd,yd,xb,yb,rb,xr,yr,rr;
int main()
{
scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",&xc,&yc,&xd,&yd,&xb,&yb,&rb,&xr,&yr,&rr);
db ans=sqrt(len(xc,yc,xr,yr)*len(xc,yc,xr,yr)-rr*rr)+sqrt(len(xd,yd,xr,yr)*len(xd,yd,xr,yr)-rr*rr);
db alpha=acos(rr/len(xc,yc,xr,yr));
db belta=acos(rr/len(xd,yd,xr,yr));
db all=acos(dot(xc-xr,yc-yr,xd-xr,yd-yr)/(len(xc,yc,xr,yr)*len(xd,yd,xr,yr)));
db in=all-alpha-belta;
ans+=rr*min(in,2*pi-in);
printf("%.10lf",ans);
return 0;
}
C、DAG上最长链,拓扑排序就完事了
我怎么dij跑负边权。。。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
typedef pair<int,int> pii;
int n,m;
vector<int> e[N],v[N];
int d[N],dp[N];
void topo()
{
queue<int> q;
for(int i=1;i<=n;++i)
if(d[i]==0) q.push(i);
while(!q.empty())
{
int f=q.front();q.pop();
for(int i=0;i<e[f].size();++i)
{
dp[e[f][i]]=max(dp[e[f][i]],dp[f]+v[f][i]);
d[e[f][i]]--;
if(d[e[f][i]]==0) q.push(e[f][i]);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,x,y,z;i<=m;++i)
{
scanf("%d%d%d",&x,&y,&z);
e[x].push_back(y);
v[x].push_back(z);
d[y]++;
}
topo();
int ans=-0x3f3f3f3f;
for(int i=1;i<=n;++i)
ans=max(ans,dp[i]);
printf("%d",ans);
return 0;
}
D、给你一串\(a[i]\),说\(a[i]=b[i]+b[i+1]\),然后要求出可能的a的数量
考虑第一个数字的范围即可,最后答案跟0取个max
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll a[1000005],b[1000005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",a+i);
ll s=0;
for(int i=1;i<=n;i++)
{
if(i&1)s+=a[i];
else s-=a[i];
b[i]=s;
}
ll l=0,r=1e18;
for(int i=1;i<=n;i++)
{
//printf("%lld ",b[i]);
if(i&1)r=min(r,b[i]);
else l=max(l,b[i]);
}
printf("%lld\n",max(0LL,r-l+1));
return 0;
}
E、给你两个实数,问两个实数的比值是否是两个质数
考虑到最多有小数点后五位,那么就乘上一个\(10^5\),但是司马的是会丢精度,\(0.00007*10^5=6.9999999\)
直接取int的话精度爆炸,所以得加上一个eps
剩下还得考虑两个相等的情况,因为两个相等可以表示成“2 2”
(怎么感觉南京H的再现)
质数暴力判就行
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps=1e-5;
int t;
double a,b;
bool isp(int x)
{
if(x==1) return 0;
for(int i=2;i*i<=x;++i)
if(x%i==0) return 0;
return 1;
}
void gao()
{
a*=1e5,b*=1e5;
a+=eps,b+=eps;
int aa=(int)a,bb=(int)b;
if(aa==bb)
{
puts("2 2");
return;
}
int g=__gcd(aa,bb);
aa/=g,bb/=g;
if(isp(aa)&&isp(bb))
{
printf("%d %d\n",aa,bb);
}
else
{
puts("impossible");
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%lf%lf",&a,&b);
gao();
}
return 0;
}
F、两个怪兽对打,怪兽造成的伤害等于自己的血量,问是否存在两个怪兽使得A打B最终使得血量分别为1 0
那么只要两个怪兽的血量是相邻的斐波那契数列两项即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,a;
int f[100];
set<int> s;
map<int,int> mp;
int main()
{
f[0]=1,f[1]=1;
int i=2;
s.insert(1);
while(f[i-1]+f[i-2]<=1e6)
{
f[i]=f[i-1]+f[i-2];
s.insert(f[i]);
i++;
}
scanf("%d",&n);
vector<int> p;
for(int i=1;i<=n;++i)
{
scanf("%d",&a);
if(s.count(a))
{
mp[a]=i;
}
if(a==1) p.push_back(i);
}
if(p.size()>=2)
{
printf("%d %d",p[0],p[1]);
return 0;
}
for(int j=0;j<i;++j)
{
if(mp[f[j]]&&mp[f[j+1]]&&mp[f[j]]!=mp[f[j+1]])
{
printf("%d %d",mp[f[j]],mp[f[j+1]]);
return 0;
}
}
puts("impossible");
return 0;
}
H、妹看
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll m,n,s;
ll qp(ll a,ll b)
{
ll ans=1;
for(;b;b>>=1,a*=a)
if(b&1)
ans*=a;
return ans;
}
int main()
{
scanf("%lld",&m);
//m=9134731356568979LL;
ll tt=(ll)(pow(m*3,1.0/3)+0.5);
for(ll t=max(1LL,tt-5);t<=tt+5;t++)
if(t*(t+1)*(2*t+1)==6*m)
return printf("3 %lld\n",t),0;
for(ll b=3;b<=54;b++)
{
ll s=0;
for(ll a=1;;a++)
{
s+=qp(a,b);
if(s==m)
return printf("%lld %lld\n",b+1,a),0;
else if(s>m)
break;
}
}
printf("impossible\n");
return 0;
}
I、暴力枚举全部加几使得A的字典序大于B
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[1005],b[1005],ans;
bool ok()
{
for(int i=1;i<=n;i++)
if(a[i]+ans>b[i])
return true;
else if(a[i]+ans<b[i])
return false;
return true;
}
int main()
{
scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",a+i);for(int i=1;i<=n;i++)scanf("%d",b+i);
while(!ok())
++ans;
printf("%d\n",ans);
return 0;
}
K、DP,枚举灰色的能拼多长
然后算橙色平均分配之后对答案的贡献
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[66],t,g;
bitset<1005>h[66];
double ans=-1;
int main()
{
h[0][0]=1;
scanf("%d%d",&n,&g);
for(int i=1;i<=n;i++)
{
scanf("%d",&t);t-=10;
for(int j=i;j>=1;j--)
h[j]|=h[j-1]<<t;
}
for(int i=1;i<=n;i++)
for(int j=max(0,g-10*(i+1));j<=g-5*(i+1);j++)
if(h[i][j])
ans=max(ans,10-(g-j)*1.0/(i+1));//,printf("%d %d\n",i,j);
if(ans!=-1)printf("%.12f",ans);else puts("impossible");
return 0;
}
L、一行一行的check,因为保证最外一圈没有雷,那么只要确定一行就可以推出下一行
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[111][111],h[111][111],b[111][111];
int dx[]={-1,-1,-1,0,0,0,1,1,1};
int dy[]={-1,0,1,-1,0,1,-1,0,1};
int main()
{
scanf("%d%d",&n,&m);++n;++n;++m;++m;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",a[i]+j);
for(int i=1;i<n;i++)
{
//h[i+1][2]=a[i][1]?1:-1;
// h[i+1][m-1]=a[i][m]?1:-1;
for(int j=1;j<m;j++)
{
int c=0;
for(int k=0;k<8;k++)c+=h[i+dx[k]][j+dy[k]]==1;
//if(c>a[i][j]||c<a[i][j]-1)return !printf("impossible");
h[i+1][j+1]=c!=a[i][j];
}
}
for(int i=1;i<=n;i++)h[i][1]=h[i][m]=h[i][0]=h[i][m+1]=0;
for(int i=1;i<=m;i++)h[1][i]=h[n][i]=h[0][i]=h[n+1][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int c=0;
for(int k=0;k<9;k++)c+=h[i+dx[k]][j+dy[k]];
//printf("%d%c",c," \n"[j==m]);
if(c!=a[i][j])return !printf("impossible");
}
for(int i=2;i<n;i++,puts(""))for(int j=2;j<m;j++)printf("%c",h[i][j]==1?'X':'.');
return 0;
}
H、跑一遍最小生成树,答案是两个点最小生成树上的最大边权
边权是两点间点权较大值
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=505*505;
const int mv[2][2]={0,1,1,0};
int n,m,dep[MAXN],lg[MAXN],fa[MAXN][32],a[505][505],val[MAXN],q,ua[MAXN];
int mx[MAXN][32];
struct edge
{
int v,x,y;
edge(){}
edge(int x,int y,int v):x(x),y(y),v(v){}
};
vector<edge> ee;
vector<int> e[MAXN];
inline bool cmp(edge a,edge b){return a.v<b.v;}
int _find(int x)
{
if(x==ua[x]) return x;
return ua[x]=_find(ua[x]);
}
inline void _merge(int x,int y)
{
x=_find(x),y=_find(y);
if(x!=y) ua[x]=y;
}
void dfs(int now,int pa)
{
dep[now]=dep[pa]+1;
fa[now][0]=pa;
mx[now][0]=max(val[now],val[pa]);
for(int i=1;(1<<i)<=dep[now];i++)
fa[now][i]=fa[fa[now][i-1]][i-1],
mx[now][i]=max(mx[now][i-1],mx[fa[now][i-1]][i-1]);
for(auto to:e[now])
if(to!=pa) dfs(to,now);
}
int lca(int x,int y)
{
int ret=val[x];
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y])
ret=max(ret,mx[x][lg[dep[x]-dep[y]]-1]),
x=fa[x][lg[dep[x]-dep[y]]-1];
if(x==y) return ret;
for(int i=lg[dep[x]]-1;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
ret=max(ret,mx[x][i]);
ret=max(ret,mx[y][i]);
x=fa[x][i],y=fa[y][i];
}
ret=max(ret,mx[x][0]);
ret=max(ret,mx[y][0]);
return ret;
}
inline int get(int x,int y)
{
return x*m+y;
}
int main()
{
for(int i=1;i<MAXN;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<n*m;i++) ua[i]=i;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&a[i][j]),val[get(i,j)]=a[i][j];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
for(int k=0;k<2;k++)
{
int dx=i+mv[k][0],dy=j+mv[k][1];
if(dx==n||dy==m) continue;
int v=max(a[i][j],a[dx][dy]);
ee.emplace_back(get(i,j),get(dx,dy),v);
}
sort(ee.begin(),ee.end(),cmp);
for(auto p:ee)
if(_find(p.x)!=_find(p.y))
{
e[p.x].push_back(p.y),e[p.y].push_back(p.x);
_merge(p.x,p.y);
}
dfs(0,n*m);
while(q--)
{
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1--,y1--,x2--,y2--;
printf("%d\n",lca(get(x1,y1),get(x2,y2)));
}
return 0;
}