zoukankan      html  css  js  c++  java
  • 洛谷P3403

    神仙建模题。

    Description

    一栋有 (h) 层的楼,初始位置为 (1),有如下操作:

    • 向上移动 (x) 层;
    • 向上移动 (y) 层;
    • 向上移动 (z) 层;
    • 回到 (1) 层。

    问可以到达的楼层数。

    Solution

    首先数据 (hle 2^{63}-1) 暴搜显然不行。然后就考虑楼层之间的关系。

    比较容易想到的是,以楼层为点来建图。但是如果直接暴力建每个楼层之间的关系的边,同样会因为数据范围太大而爆炸。

    所以我们换个思路,考虑给出的操作之间的关系。

    我们可以先处理其中两种上升操作,然后将这些情况求出来,再反过来找第三种上升操作能达到的新楼层。

    这里设 ( ext{dis}(i)) 表示只用操作 (2)(3) 能到达的在 (mod x) 意义下能到达 (i) 的最小楼层。

    然后可以得出:

    (egin{cases} ext{dis}[(i+y)mod x]=min{ ext{dis}(imod x)+y}\ ext{dis}[(i+z)mod x]=min{ ext{dis}(imod x)+z} end{cases})

    这和我们的最短路更新方式很相似。

    我们就可以考虑将 (i+y,i+z) 作为点,用最短路求出所有的 ( ext{dis}(i)) ((iin [0,x)))

    接下来我们考虑 ( ext{dis}) 的作用。

    如果我们知道只通过操作 (2,3) 能到达的最小高度,这说明在此基础上能达到的剩下的楼层只需要由操作 (1) 来补全即可。

    如果上面的话不理解可以手动模拟一下。而且可以保证没有漏掉的情况。

    所以求出所有的 ( ext{dis}) 之后,答案就是 (large{sum_{i=0}^{x-1}[frac{(h- ext{dis}(i))}{x}+1]}) (( ext{dis}(i)le h))

    上面这个式子也可以手动模拟一下,应该不是很难理解。

    至于为什么每次答案要加一,因为我们每次统计答案要加上 ( ext{dis}(i)) 这层本身。

    Other things

    但是还是存在 (h) 太大的问题,并且考虑到真正需要的下标都在 ([0,x)) 范围内,所以我们只在这个范围内建边就好了。

    另外要注意,因为楼层高度范围是 ([1,h]),所以 ( ext{dis}(1)=1)

    最后,如果 (x,y,z) 中有至少一个是 (1),就说明所有的楼层都可达,此时上面的都是扯淡,答案是 (h)

    剩下的细节都在代码里(也没什么细节)。

    Code

    #include<queue>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define maxn 200100
    #define INF 0x3f3f3f3f
    #define int long long
    
    using namespace std;
    
    bool vis[maxn];
    int h,x,y,z,tot,ans;
    int Dis[maxn],head[maxn];
    struct edge{int fr,to,dis,nxt;}e[maxn<<2];
    
    int read(){
      int s=0,w=1;char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
      while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
      return s*w;
    }
    
    void add(int fr,int to,int dis){
      e[++tot]=(edge){fr,to,dis,head[fr]};head[fr]=tot;
    }
    
    void SPFA(){
      memset(Dis,INF,sizeof Dis);
      memset(vis,false,sizeof vis);
      deque<int> q;q.push_back(1);
      Dis[1]=1;vis[1]=true;
      while(!q.empty()){
        int u=q.front();
        q.pop_front();vis[u]=false;
        for(int i=head[u];i;i=e[i].nxt){
          int to=e[i].to;
          if(Dis[to]>Dis[u]+e[i].dis){
            Dis[to]=Dis[u]+e[i].dis;
            if(!vis[to]){
              vis[to]=1;
              if(!q.empty()&&Dis[q.front()]>Dis[to])q.push_front(to);
              else q.push_back(to);
            }
          }
        }
      }
    }
    
    signed main(){
      h=read();
      x=read();y=read();z=read();
      if(x==1||y==1||z==1){cout<<h;return 0;}
      for(int i=0;i<x;i++)
        add(i,(i+y)%x,y),add(i,(i+z)%x,z);
      SPFA();
      for(int i=0;i<x;i++)
        if(Dis[i]<=h) ans+=(h-Dis[i])/x+1;
      printf("%lld
    ",ans);
      return 0;
    }
    
    
  • 相关阅读:
    Elasticsearch 缓存总结
    ElasticSearch-集群
    HTTP协议详解
    HTTPS总结
    ElasticSearch--Document
    正排索引和倒排索引
    线上OOM排查步骤总结
    线程池-四种拒绝策略总结
    netty篇-练手
    netty篇-UDP广播
  • 原文地址:https://www.cnblogs.com/KnightL/p/14876338.html
Copyright © 2011-2022 走看看