gym101666題解

  • 2019 年 10 月 5 日
  • 筆記

A Amsterdam Distance

題意

求圓環上的兩點距離。

分析

顯然是沿半徑方向走到內圈再走圓弧最短。

程式碼

#include <bits/stdc++.h>  using namespace std;  double m,n,r,sx,sy,tx,ty;  const double pi=acos(-1.0);  int main(){      scanf("%lf%lf%lf%lf%lf%lf%lf",&m,&n,&r,&sx,&sy,&tx,&ty);      double ans=fabs(ty-sy)*r/n;      double mr=min(sy,ty)*r/n;      double ps=fabs(tx-sx);      ans+=ps*pi*mr/m;      printf("%.14lfn",min(ans,(ty+sy)*r/n));      return 0;  }

C Collatz Conjecture

題意

n個數,求所有不同區間的gcd值的不同個數。

分析

  • 序列的gcd問題經常就是暴力亂搞…不過姿勢要對,因為gcd降得很快。
  • 考慮暴力,我們從長度為1的區間依次合併,即每次a[i]=gcd(a[i],a[i+1]);,然後直接對a數組去重,比如6 4 2 2,此時兩個長度為3的區間的gcd值都是2,所以直接去重捨棄第二個,因為整個序列從頭開始的gcd值一定是不增的,所以直接捨棄掉後面小的是正確的。

程式碼

#include <bits/stdc++.h>  using namespace std;  typedef long long ll;  const int N=5e5+50;  int n;  ll a[N];  ll gcd(ll a,ll b){      return b==0?a:gcd(b,a%b);  }  set<ll> s;  int main(){      scanf("%d",&n);      for(int i=1;i<=n;i++){          scanf("%lld",&a[i]);          s.insert(a[i]);      }      for(int r=n;r>1;r--){          for(int i=1;i<r;i++){              a[i]=gcd(a[i],a[i+1]);              s.insert(a[i]);          }          r=unique(a+1,a+r)-a;      }      printf("%dn",int(s.size()));      return 0;  }

D Detour

題意

給一個圖,對每個節點不能走該節點到終點最短路方向的邊,求路徑。

分析

從終點跑一遍最短路,相當於多源到終點的最短路,然後刪除最短路邊(單向邊),再從起點跑一遍最短路。

程式碼

#include <bits/stdc++.h>  using namespace std;  typedef long long ll;  const int N=2e5+50;  const int M=2e6+50;  struct Edge{      int u,v;      ll w;      int next,vis;  }e[M*2];  int cnt,head[N];  void init(){      cnt=0;      memset(head,-1,sizeof(head));  }  void add(int u,int v,ll w){      e[cnt]=Edge{u,v,w,head[u],0};      head[u]=cnt++;      e[cnt]=Edge{v,u,w,head[v],0};      head[v]=cnt++;  }  int s,t,n,m,u,v,p[N];  ll w;  int vis[N];  ll dis[N];  const ll INF=1e18;  struct node{      int v;      ll w;      bool operator<(const node& rhs)const{          return w>rhs.w;      }  };  void dijk(bool del){      for(int i=1;i<=n;i++){          vis[i]=0;          dis[i]=INF;          p[i]=-1;      }      dis[s]=0;      priority_queue<node> q;      q.push(node{s,0});      while(!q.empty()){          node t=q.top();          q.pop();          int u=t.v;          if(vis[u]){              continue;          }          vis[u]=1;          for(int i=head[u];i!=-1;i=e[i].next){              int v=e[i].v;              ll w=e[i].w;              if(del && e[i].vis){                  continue;              }              if(!vis[v] && dis[v]>dis[u]+w){                  dis[v]=dis[u]+w;                  p[v]=u;                  q.push(node{v,dis[v]});              }          }      }  }  int ans[N];  int main(){      //freopen("in.txt","r",stdin);      scanf("%d%d",&n,&m);      init();      for(int i=1;i<=m;i++){          scanf("%d%d%lld",&u,&v,&w);          u++;          v++;          add(u,v,w);      }      s=2,t=1;      dijk(0);  //    for(int u=1;u<=n;u++){  //        for(int i=head[u];i!=-1;i=e[i].next){  //            int v=e[i].v;  //            if(p[u]==v){  //                e[i].vis=1;  //            }  //        }  //    }      for(int i=0;i<cnt;i++){          int u=e[i].u;          int v=e[i].v;          ll w=e[i].w;          //printf("%d %d %lld %lldn",u,v,abs(dis[u]-dis[v]),w);          if(dis[u]-dis[v]==w){              e[i].vis=1;              //printf("%d %dn",u,v);          }      }      s=1,t=2;      dijk(1);      if(dis[t]==INF){          printf("impossiblen");      }else{          int tot=0;          for(int i=t;i!=-1;i=p[i]){              ans[tot++]=i-1;          }          printf("%d",tot);          for(int i=tot-1;i>=0;i--){              printf(" %d",ans[i]);          }          printf("n");      }      return 0;  }

E Easter Eggs

題意

有b個藍色點和r個紅色點,將n個物品分配到這兩種點上,使得紅色點上的物品和藍色點上的物品的最小距離最大。

分析

  • 最小距離最大,考慮二分答案,即二分最小距離,轉化為使得兩種點的任意兩點的距離都大於mid。
  • 兩種點考慮二分圖,對點之間距離小於mid的建邊,那我們要求的就是該二分圖的最大點獨立集,即兩兩之間沒有邊,也就是距離大於mid。

程式碼

#include <bits/stdc++.h>  using namespace std;  typedef long long ll;  const int N=1005;  int n,b,r;  int vis[N],mt[N];  int g[N][N];  ll x[N],y[N],cost[N][N];  bool dfs(int u,int n){      for(int i=1;i<=n;i++){          if(!vis[i] && g[u][i]){              vis[i]=1;              if(mt[i]==-1 || dfs(mt[i],n)){                  mt[i]=u;                  return true;              }          }      }      return false;  }  int solve(){      int ans=0;      memset(mt,-1,sizeof(mt));      for(int i=1;i<=b;i++){          memset(vis,0,sizeof(vis));          ans+=dfs(i,r);      }      return ans;  }  bool check(ll dis){      memset(g,0,sizeof(g));      for(int i=1;i<=b;i++){          for(int j=1;j<=r;j++){              if(cost[i][j]<dis){                  g[i][j]=1;              }          }      }      int sol=solve();      return b+r-sol>=n;  }  int main(){      //freopen("in.txt","r",stdin);      scanf("%d%d%d",&n,&b,&r);      for(int i=1;i<=b+r;i++){          scanf("%lld%lld",&x[i],&y[i]);      }      for(int i=1;i<=b;i++){          for(int j=1;j<=r;j++){              cost[i][j]=1ll*(x[i]-x[b+j])*(x[i]-x[b+j])+1ll*(y[i]-y[b+j])*(y[i]-y[b+j]);          }      }      ll L=0,R=1e18+50;      ll ans=0;      while(L<=R){          ll mid=(L+R)/2;          if(check(mid)){              L=mid+1;              ans=mid;          }else{              R=mid-1;          }      }      printf("%.10lfn",sqrt(ans*1.0));      return 0;  }

F Falling Apart

題意

n個數,兩人輪流取,兩個人都要最大。

分析

排序。

程式碼

#include <bits/stdc++.h>  using namespace std;  const int N=20;  int a[N],n;  int main(){      scanf("%d",&n);      for(int i=1;i<=n;i++){          scanf("%d",&a[i]);      }      sort(a+1,a+1+n);      int aa=0,bb=0;      int f=0;      for(int i=n;i>=1;i--){          if(f){              bb+=a[i];          }else{              aa+=a[i];          }          f^=1;      }      printf("%d %dn",aa,bb);      return 0;  }

I Irrational Division

題意

有一個p*q的黑白相間的格子圖,你站在左邊往右切,另一個人站在下邊往上切,切到的部分黑色加1分,白色減1分,兩人都按最優策略盡量讓自己分最大,問最大可能的分數差值絕對值。

分析

  • 顯然分奇偶判斷一下。
  • 當p為偶數,列無論怎麼取都是黑白相同0,所以最優取法是保守取剩下偶數列,這樣對方也只能保守取偶數行,最後答案為0。如果取剩下奇數列,那最後的得分是0和-2,雖然答案變大,但是並不是最優的使得兩人得分盡量多的策略。
  • 當p為奇數,q也為奇數時,第一步保守取偶數列得分0,剩下奇數列,對方顯然可以只選一行,得1分,最後結果是0和1,若第一步激進選奇數列得1分,考慮直接取完的情況,所以最後結果是1和0,所以答案為1。
  • 當p為奇數,q為偶數時,還需要判斷p和q的關係。
  • 當p<q時,第一步保守取偶數列得0分,對方最優肯定也是保守取1行,轉化為pq均為偶數的情況,答案為0,第一步激進取1列得1分,對方只能保守取偶數行得0分,我們再保守取,最後肯定能剩下一個白色角落給對方,因此最後結果是0和-2,答案為2。
  • 當p>q時,第一步保守取偶數列得0分,對方最優肯定取奇數行剩下偶數行,轉化為pq均為偶數的情況,答案為0,第一步激進取1列得1分,對方最優取偶數行剩下奇數行,我們只能保守取偶數列,最後肯定剩下一個白色角落給我們,因此最後結果是0和0,答案為0。

程式碼

#include <bits/stdc++.h>  using namespace std;  int p,q;  int main(){      scanf("%d%d",&p,&q);      if(p%2==0 && q%2==0){          printf("0n");      }else if(p%2==0 && q%2==1){          printf("0n");      }else if(p%2==1 && q%2==0){          if(p<q){              printf("2n");          }else{              printf("0n");          }      }else if(p%2==1 && q%2==1){          printf("1n");      }      return 0;  }

K King of the Waves

題意

給n個人的對戰勝負關係,要求構造一個擂台挑戰順序使得0號贏。

分析

  • 從0號開始按照勝負關係遞歸下去,如果能訪問到每個點,那就可以,按dfs序輸出即可。

程式碼

#include <bits/stdc++.h>  using namespace std;  const int N=1e3+50;  char s[N][N];  int w[N][N],vis[N];  int n,cnt;  vector<int> ans;  void dfs(int u){      cnt++;      vis[u]=1;      for(int i=0;i<n;i++){          if(vis[i] || w[u][i]!=1){              continue;          }          dfs(i);      }      ans.push_back(u);  }  int main(){      //freopen("in.txt","r",stdin);      scanf("%d",&n);      for(int i=0;i<n;i++){          scanf("%s",s[i]);          for(int j=0;j<n;j++){              if(s[i][j]=='X'){                  w[i][j]=-1;              }else{                  w[i][j]=s[i][j]-'0';              }          }      }      dfs(0);      if(cnt==n){          for(int i=0;i<n;i++){              printf("%d%c",ans[i],i==n-1?'n':' ');          }      }else{          printf("impossiblen");      }      return 0;  }

L Lemonade Trade

題意

初始有1升pink,給n個交易關係,問最後最多能換多少blue。

分析

  • 一開始看錯題意以為可以隨便交易,那就是建圖跑。
  • 由於只能按順序交易,那麼直接把字元串映射一下然後簡單dp即可,注意把乘法改成加log,然後最後判斷大於10也不能先計算結果再判斷,因為2的1e5次方太大,所以直接用log判斷。

程式碼

#include <bits/stdc++.h>  using namespace std;  const int N=2e5+50;  const double INF=1e18;  map<string,int> idx;  int n;  char a[15],b[15];  double w;  double mx[N];  int vis[N];  int main(){      //freopen("in.txt","r",stdin);      int tot=0;      scanf("%d",&n);      idx["pink"]=++tot;      vis[tot]=1;      for(int i=1;i<N;i++){          mx[i]=-INF;      }      mx[1]=0.0;      for(int i=1;i<=n;i++){          scanf("%s%s%lf",a,b,&w);          if(idx.find(a)==idx.end()){              idx[a]=++tot;          }          if(idx.find(b)==idx.end()){              idx[b]=++tot;          }          if(vis[idx[b]]){              mx[idx[a]]=max(mx[idx[a]],mx[idx[b]]+log2(w));              vis[idx[a]]=1;          }      }      int t=idx["blue"];      if(vis[t]){          if(mx[t]>log2(10.0)){              printf("10.000000000n");          }else{              printf("%.14lfn",min(10.000000000,pow(2.0,mx[idx["blue"]])));          }      }else{          printf("0.0000000000n");      }      return 0;  }

M Manhattan Mornings

題意

給定起點終點,求二維的最長不下降子序列。

分析

  • 一維排序,按順序枚舉,然後另一位離散化後在起點終點範圍內的點加入線段樹中,並查詢最大值更新。
  • 注意起點終點的四種可能位置。

程式碼

#include <bits/stdc++.h>  using namespace std;  const int N=2e5+50;  struct Orz{      vector<int> a;      void clr(){          a.clear();      }      int siz(){          return a.size();      }      void add(int x){          a.push_back(x);      }      void work(){          sort(a.begin(),a.end());          a.erase(unique(a.begin(),a.end()),a.end());      }      int idx(int x){          return lower_bound(a.begin(),a.end(),x)-a.begin()+1;      }      int val(int i){          return a[i-1];      }  }orz;  struct ST{  #define ls i<<1  #define rs i<<1|1  #define mid (l+r)/2      int mx[N*4];      void pushup(int i){          mx[i]=max(mx[ls],mx[rs]);      }      void build(int i,int l,int r){          mx[i]=0;          if(l==r){              return;          }          build(ls,l,mid);          build(rs,mid+1,r);      }      void update(int i,int l,int r,int p,int v){          if(l==r && l==p){              mx[i]=max(mx[i],v);              return;          }          if(p<=mid){              update(ls,l,mid,p,v);          }else{              update(rs,mid+1,r,p,v);          }          pushup(i);      }      int query(int i,int l,int r,int ql,int qr){          if(ql<=l && qr>=r){              return mx[i];          }          int ans=0;          if(ql<=mid){              ans=max(ans,query(ls,l,mid,ql,qr));          }          if(qr>mid){              ans=max(ans,query(rs,mid+1,r,ql,qr));          }          return ans;      }  }ac;  struct node{      int x,y,w;  }a[N];  bool cmp1(node a,node b){      if(a.y!=b.y){          return a.y<b.y;      }else{          return a.x<b.x;      }  }  bool cmp2(node a,node b){      if(a.y!=b.y){          return a.y<b.y;      }else{          return a.x>b.x;      }  }  int n,sx,sy,tx,ty,nx[N],ny[N];  int main(){      //freopen("in.txt","r",stdin);      scanf("%d",&n);      scanf("%d%d%d%d",&sx,&sy,&tx,&ty);      orz.clr();      orz.add(sx);      orz.add(sy);      orz.add(tx);      orz.add(ty);      for(int i=1;i<=n;i++){          scanf("%d%d",&nx[i],&ny[i]);          orz.add(nx[i]);          orz.add(ny[i]);      }      orz.work();      int ns=orz.siz();      sx=orz.idx(sx);      sy=orz.idx(sy);      tx=orz.idx(tx);      ty=orz.idx(ty);      if(sy>ty){          swap(sx,tx);          swap(sy,ty);      }      a[0]=node{sx,sy};      a[1]=node{tx,ty};      for(int i=1;i<=n;i++){          nx[i]=orz.idx(nx[i]);          ny[i]=orz.idx(ny[i]);          a[i+1]=node{nx[i],ny[i]};      }      if(sx<tx){          sort(a,a+n+2,cmp1);          for(int i=0;i<=n+1;i++){              if(a[i].x>=sx && a[i].x<=tx && a[i].y>=sy && a[i].y<=ty){                  int x=a[i].x;                  int t=ac.query(1,1,ns,1,x);                  a[i].w=t+1;                  ac.update(1,1,ns,x,a[i].w);              }          }          printf("%dn",ac.query(1,1,ns,sx,tx)-2);      }else{          sort(a,a+n+2,cmp2);          for(int i=0;i<=n+1;i++){              if(a[i].x>=tx && a[i].x<=sx && a[i].y>=sy && a[i].y<=ty){                  int x=a[i].x;                  int t=ac.query(1,1,ns,x,ns);                  a[i].w=t+1;                  ac.update(1,1,ns,x,a[i].w);              }          }          printf("%dn",ac.query(1,1,ns,tx,sx)-2);      }      return 0;  }