1. 程式人生 > 其它 >洛谷P3403

洛谷P3403

神仙建模題。

Description

一棟有 \(h\) 層的樓,初始位置為 \(1\),有如下操作:

  • 向上移動 \(x\) 層;
  • 向上移動 \(y\) 層;
  • 向上移動 \(z\) 層;
  • 回到 \(1\) 層。

問可以到達的樓層數。

Solution

首先資料 \(h\le 2^{63}-1\) 暴搜顯然不行。然後就考慮樓層之間的關係。

比較容易想到的是,以樓層為點來建圖。但是如果直接暴力建每個樓層之間的關係的邊,同樣會因為資料範圍太大而爆炸。

所以我們換個思路,考慮給出的操作之間的關係。

我們可以先處理其中兩種上升操作,然後將這些情況求出來,再反過來找第三種上升操作能達到的新樓層。

這裡設 \(\text{dis}(i)\)

表示只用操作 \(2\)\(3\) 能到達的在 \(\bmod x\) 意義下能到達 \(i\) 的最小樓層。

然後可以得出:

\(\begin{cases} \text{dis}[(i+y)\bmod x]=\min\{\text{dis}(i\bmod x)+y\}\\ \text{dis}[(i+z)\bmod x]=\min\{\text{dis}(i\bmod x)+z\} \end{cases}\)

這和我們的最短路更新方式很相似。

我們就可以考慮將 \(i+y,i+z\) 作為點,用最短路求出所有的 \(\text{dis}(i)\) \((i\in [0,x))\)

接下來我們考慮 \(\text{dis}\)

的作用。

如果我們知道只通過操作 \(2,3\) 能到達的最小高度,這說明在此基礎上能達到的剩下的樓層只需要由操作 \(1\) 來補全即可。

如果上面的話不理解可以手動模擬一下。而且可以保證沒有漏掉的情況。

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

上面這個式子也可以手動模擬一下,應該不是很難理解。

至於為什麼每次答案要加一,因為我們每次統計答案要加上 \(\text{dis}(i)\) 這層本身。

Other things

但是還是存在 \(h\) 太大的問題,並且考慮到真正需要的下標都在 \([0,x)\) 範圍內,所以我們只在這個範圍內建邊就好了。

另外要注意,因為樓層高度範圍是 \([1,h]\),所以 \(\text{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\n",ans);
  return 0;
}