oneman233

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;
}

posted on 2019-11-01 20:39  oneman233  阅读(292)  评论(0编辑  收藏  举报

导航