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

猜你喜欢

转载自www.cnblogs.com/oneman233/p/11779497.html