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

 

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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
*/
View Code

 

 

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

更美妙的方法:依然是建立连通关系,不过我们是建立一棵树,从小到大连通,每次把之前的连通块的根作为当前点的儿子。 最后就可以在线询问了,询问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;
}
View Code

 

posted @ 2019-01-31 20:40  nimphy  阅读(521)  评论(0编辑  收藏