1. 程式人生 > >P3645 [APIO2015]雅加達的摩天樓

P3645 [APIO2015]雅加達的摩天樓

ace space 而且 base .com 包含 永遠 們的 %d

題目描述

印尼首都雅加達市有 NN 座摩天樓,它們排列成一條直線,我們從左到右依次將它們編號為 00 到 $N ? 1$。除了這 NN 座摩天樓外,雅加達市沒有其他摩天樓。

MM 只叫做 “doge” 的神秘生物在雅加達市居住,它們的編號依次是 00 到 $M ? 1$。編號為 ii 的 doge 最初居住於編號為 B_iBi? 的摩天樓。每只 doge 都有一種神秘的力量,使它們能夠在摩天樓之間跳躍,編號為 ii 的 doge 的跳躍能力為 P_iPi?P_i > 0Pi?>0)。

在一次跳躍中,位於摩天樓 bb 而跳躍能力為 pp 的 doge 可以跳躍到編號為 $b ? p$ (如果 $0 \leq b ? p < N$)或 b + pb+p(如果 0 \leq b + p < N0b+p<N)的摩天樓。

編號為 00 的 doge 是所有 doge 的首領,它有一條緊急的消息要盡快傳送給編

號為 11 的 doge。任何一個收到消息的 doge 有以下兩個選擇:

跳躍到其他摩天樓上;

將消息傳遞給它當前所在的摩天樓上的其他 doge。

請幫助 doge 們計算將消息從 00 號 doge 傳遞到 11 號 doge 所需要的最少總跳躍步數,或者告訴它們消息永遠不可能傳遞到 11 號 doge。

輸入輸出格式

輸入格式:

輸入的第一行包含兩個整數 NN 和 MM。

接下來 MM 行,每行包含兩個整數 B_iBi?P_iPi?

輸出格式:

輸出一行,表示所需要的最少步數。如果消息永遠無法傳遞到 11 號 doge,輸出 $?1$。

輸入輸出樣例

輸入樣例#1:
5 3
0 2
1 1
4 1
輸出樣例#1:
5

說明

【樣例解釋】

下面是一種步數為 55 的解決方案:

00 號 doge 跳躍到 22 號摩天樓,再跳躍到 44 號摩天樓(22 步)。

00 號 doge 將消息傳遞給 22 號 doge。

22 號 doge 跳躍到 33 號摩天樓,接著跳躍到 22 號摩天樓,再跳躍到 11 號摩天樓(33 步)。

22 號 doge 將消息傳遞給 11 號 doge。

【數據範圍】

所有數據都保證 0 \leq B_i < N0Bi?<N。

子任務 1 (10 分)1 \leq N \leq 101N10

1 \leq P_i \leq 101Pi?10

2 \leq M \leq 32M3

子任務 2 (12 分)1 \leq N \leq 1001N100

1 \leq P_i \leq 1001Pi?100

2 \leq M \leq 20002M2000

子任務 3 (14 分)1 \leq N \leq 20001N2000

$1 \leq P i ≤ 2000$

2 \leq M \leq 20002M2000

子任務 4 (21 分)1 \leq N \leq 20001N2000

1 \leq P_i \leq 20001Pi?2000

2 \leq M \leq 300002M30000

子任務 5 (43 分)1 \leq N \leq 300001N30000

1 \leq P_i \leq 300001Pi?30000

2 \leq M \leq 300002M30000

題解:

哇哇哇這道題A掉真不容易

這道題看到就想著建圖,但是直接建肯定是不行的。

為啥不行呢?原來,對於某一個doge的p值,我們的建圖方法是這樣滴:

技術分享

如果p值較大的話,那還好辦,因為不會連出去多少邊

但是,如果p值小的話,那麽每一個點可能會往外連好多個邊,而且可能有重復的!

復雜度可能接近 n2 哦

那麽,如何改進建邊方法?

老師教我們做分層圖(似乎也叫分塊?並不太清楚……)

對於p值較大的,比如大於sqrt(n)的,我們還直接建邊

但是對於p值較小的,我們就對於不同的p值分別建圖,然後每兩個可一步到達的“相鄰”點間都連正反兩條邊

就像這樣:

p=1層的圖:

技術分享

p=2層的圖:

技術分享

(畫圖好累……)

好了大概層裏建圖就是這樣

可是不能光在一個層裏面跑啊,得在不同層間跑

那麽不同層間的邊怎麽連?

不同層間的邊其實就相當於一棟樓中有些可跳距離不同的doge,那對於每一個doge都把同一個點不同層的點指向該點(這樣說好抽象啊……看代碼應該好理解些)

這樣建圖復雜度是nlogn的

這樣圖建完後跑最短路就可以了

我一開始用dijkstra堆優化,但是始終T一個點。後來改成SPFA又修改了許多耗時的地方才A掉……95分了好長時間……

我做這道題時中間有一段時間一直65分,因為我在建圖時有一塊兒想錯了

我把p值較大的暴力建圖時是按照p值較小的建圖方法建的(如上2圖)

但是這樣是有問題的

技術分享

兩者的區別不單是前者邊少後者邊多,更是前者只能從一個點出發去其他點,而後者可從每一個點出發去其他點!

雖然感覺上求兩點之間距離這兩種方法是一樣的,但當有其他點、邊或其他操作插進來後就很不一樣了

比如前一個圖,第三個點與第四個點是無法互相到達的,而在後面圖中就可以

下次寫題是一定要註意這一點!要想清楚了!

代碼:

技術分享
  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<math.h>
  5 #include<queue>
  6 #define INF 1000000007
  7 using namespace std;
  8 
  9 const int MAXN = 30005;
 10 struct node{
 11     int v,len,lev;
 12     node *next;       
 13 }pool[500*MAXN],*h[400][MAXN];
 14 int cnt;
 15 
 16 int read(){
 17     int x=0,f=1;
 18     char ch=getchar();
 19     while(ch>9 || ch<0) ch=getchar();
 20     while(0<=ch && ch<=9) x=x*10+ch-0,ch=getchar();
 21     return x;
 22 }
 23 
 24 void addedge(int u,int lu,int v,int lv,int len){
 25     node *p=&pool[++cnt];
 26     p->v=v;p->len=len;p->lev=lv;
 27     p->next=h[lu][u];h[lu][u]=p;     
 28 }
 29 
 30 int n,m,sn;
 31 int b[MAXN],p[MAXN],vis[405];
 32 
 33 int d[400][MAXN],use[400][MAXN];
 34 struct qqq{
 35     int num,lev;       
 36 };
 37 queue<qqq> que1;
 38 void spfa(int S,int lS,int T){
 39     int u,v,l;
 40     qqq newq,now;
 41     for(int i=0;i<sn;i++)
 42         for(int j=0;j<n;j++) d[i][j]=INF; 
 43     while(!que1.empty()) que1.pop();
 44     d[lS][S]=0;
 45     newq.num=S;newq.lev=lS;
 46     que1.push(newq);
 47     while(!que1.empty()){
 48         now=que1.front();que1.pop();
 49         u=now.num;l=now.lev;
 50         for(node *p=h[l][u];p;p=p->next)
 51         {
 52             v=p->v;
 53             if(d[p->lev][v]>d[l][u]+p->len){
 54                 d[p->lev][v]=d[l][u]+p->len;
 55                 if(use[p->lev][v]) continue;
 56                 newq.num=v;newq.lev=p->lev;
 57                 que1.push(newq);
 58                 use[p->lev][v]=1;
 59             }
 60         }
 61         use[l][u]=0;
 62     } 
 63 }
 64 
 65 int main()
 66 {
 67     int i,j,l2,S,T,lS;
 68     n=read();m=read();
 69     for(i=0;i<m;i++) b[i]=read(),p[i]=read();
 70     sn=min((int)sqrt(n),100);
 71     S=b[0];T=b[1];
 72     if(p[0]<sn) lS=p[0];else lS=0;
 73     
 74     //addedge
 75     for(i=0;i<m;i++){
 76         if(p[i]>=sn){
 77             vis[0]=1;
 78             for(j=b[i]%p[i];j<n;j+=p[i]){
 79                 if(j==b[i]) continue;
 80                 addedge(b[i],0,j,0,abs(j-b[i])/p[i]);
 81             } 
 82         }
 83         else vis[p[i]]=1;
 84     }
 85     for(i=1;i<sn;i++)
 86         if(vis[i]){
 87             for(j=0;j+i<n;j++){
 88                 addedge(j,i,j+i,i,1);
 89                 addedge(j+i,i,j,i,1);       
 90             }
 91         }
 92     //level
 93     for(i=0;i<m;i++){
 94         if(p[i]<sn) l2=p[i];
 95         else l2=0;
 96         for(j=0;j<sn;j++) 
 97             if(j!=l2 && vis[j]){ 
 98                 addedge(b[i],j,b[i],l2,0);
 99             }
100     }
101 
102     //spfa
103     spfa(S,lS,T);
104     int ans=INF;
105     for(i=0;i<sn;i++) ans=min(ans,d[i][T]);
106     if(ans==INF) printf("-1\n");
107     else printf("%d\n",ans);
108 
109     return 0;    
110 }
View Code

P3645 [APIO2015]雅加達的摩天樓