题目描述

印尼首都雅加达市有 $N$ 座摩天楼,它们排列成一条直线,我们从左到右依次将它们编号为 $0$ 到 $N − 1$。除了这 $N$ 座摩天楼外,雅加达市没有其他摩天楼。

有 $M$ 只叫做 “doge” 的神秘生物在雅加达市居住,它们的编号依次是 $0$ 到 $M − 1$。编号为 $i$ 的 doge 最初居住于编号为 $B_i$ 的摩天楼。每只 doge 都有一种神秘的力量,使它们能够在摩天楼之间跳跃,编号为 $i$ 的 doge 的跳跃能力为 $P_i$ ($P_i > 0$)。

在一次跳跃中,位于摩天楼 $b$ 而跳跃能力为 $p$ 的 doge 可以跳跃到编号为 $b − p$ (如果 $0 \leq b − p < N$)或 $b + p$ (如果 $0 \leq b + p < N$)的摩天楼。

编号为 $0$ 的 doge 是所有 doge 的首领,它有一条紧急的消息要尽快传送给编
号为 $1$ 的 doge。任何一个收到消息的 doge 有以下两个选择:

  1. 跳跃到其他摩天楼上;
  2. 将消息传递给它当前所在的摩天楼上的其他 doge。

请帮助 doge 们计算将消息从 $0$ 号 doge 传递到 $1$ 号 doge 所需要的最少总跳跃步数,或者告诉它们消息永远不可能传递到 $1$ 号 doge。

输入格式

输入的第一行包含两个整数 $N$ 和 $M$。

接下来 $M$ 行,每行包含两个整数 $B_i$ 和 $P_i$。

输出格式

输出一行,表示所需要的最少步数。如果消息永远无法传递到 $1$ 号 doge,输出 $−1$。

子任务

所有数据都保证 $0 \leq B_i < N$。

  • 子任务 1 (10 分)
    • $1 \leq N \leq 10$
    • $1 \leq P_i \leq 10$
    • $2 \leq M \leq 3$
  • 子任务 2 (12 分)
    • $1 \leq N \leq 100$
    • $1 \leq P_i \leq 100$
    • $2 \leq M \leq 2000$
  • 子任务 3 (14 分)
    • $1 \leq N \leq 2000$
    • $1 \leq P i ≤ 2000$
    • $2 \leq M \leq 2000$
  • 子任务 4 (21 分)
    • $1 \leq N \leq 2000$
    • $1 \leq P_i \leq 2000$
    • $2 \leq M \leq 30000$
  • 子任务 5 (43 分)
    • $1 \leq N \leq 30000$
    • $1 \leq P_i \leq 30000$
    • $2 \leq M \leq 30000$

分析1

并不想思考前三部分分着怎么打啊?

很直观的就可以想到,在可以达到的doge之间连有向边,代价为方向上的走向所需要的步数

直接跑一遍裸的dijkstra就有36分了咯.....

为了代码文章看起来好一点,就把头文件去掉了

int n,m,b[30010],p[30010];
int a[2010][2010],f[30010];
bool v[30010];
const int inf=100000000; int main()
{
read(n);read(m);
for (int i=1;i<=m;i++)
read(b[i]),read(p[i]);
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
a[i][j]=inf;
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
if (i!=j && abs(b[i]-b[j])%p[i]==0)
a[i][j]=abs(b[i]-b[j])/p[i];
for (int i=1;i<=m;i++)
f[i]=inf;
f[1]=0;int x=1;
memset(v,0,sizeof(v));
for (int i=1;i<=m;i++)
{
for (int j=1;j<=m;j++)
if (f[x]+a[x][j]<f[j]) f[j]=f[x]+a[x][j];
v[x]=1;x=0;
for (int j=1;j<=m;j++)
if (!v[j]&&(x==0||f[x]>f[j])) x=j;
}
if (f[2]==inf) puts("-1");else print(f[2]);
return 0;
}

分析2

思考bfs

用数组\(f[i][j]\)表示\({doge}_{i}\)到建筑\(j\)的最少步数

初始\(f[0][B_0]=0\),每次用\(f[i][j]\)去尝试拓展\(f[i][j\pm P_i]\)和位置\(j\pm P_i\)其他doge

一旦拓展到\({doge}_{1}\),直接输出答案即可,时间复杂度\(O(NM)\),期望得分57

代码同样,暂时去掉了头文件

int n,m,p[30010],f[30001][2001];
vector<int> b[2010];
const int inf=100000000;
struct data
{
int x,y;
data(int a=0,int b=0):x(a),y(b){}
};
queue<data> q;
bool v[2010]; int main()
{
read(n);read(m);
int x,y;
for (int i=1;i<=m;i++)
{
read(x),read(p[i]),b[x].push_back(i);
if (i==1) y=x;
}
memset(v,0,sizeof(v));
for (int i=1;i<=m;i++)
for (int j=0;j<n;j++)
f[i][j]=inf;
v[y]=1;
for (int i=0;i<b[y].size();i++)
q.push(data(b[y][i],y)),f[b[y][i]][y]=0;
while(!q.empty())
{
int x=q.front().x,y=q.front().y;q.pop();
if (x==2){print(f[x][y]);return 0;}
if (y-p[x]>=0 && !v[y-p[x]])
{
v[y-p[x]]=1;
for (int i=0;i<b[y-p[x]].size();i++)
f[b[y-p[x]][i]][y-p[x]]=f[x][y]+1,q.push(data(b[y-p[x]][i],y-p[x]));
}
if (y+p[x]<n && !v[y+p[x]])
{
v[y+p[x]]=1;
for (int i=0;i<b[y+p[x]].size();i++)
f[b[y+p[x]][i]][y+p[x]]=f[x][y]+1,q.push(data(b[y+p[x]][i],y+p[x]));
}
if (y-p[x]>=0)
{
if (f[x][y]+1<f[x][y-p[x]]) f[x][y-p[x]]=f[x][y]+1,q.push(data(x,y-p[x]));
}
if (y+p[x]<n)
{
if (f[x][y]+1<f[x][y+p[x]]) f[x][y+p[x]]=f[x][y]+1,q.push(data(x,y+p[x]));
}
}
puts("-1");
return 0;
}

分析3

一口毒奶......这题好毒啊......

考虑分块来做

当\(P_i>\sqrt n\)时,可以直接暴力来做,因为最多只会有\(n\sqrt n\)条边

当\(P_i\le \sqrt n\)时,我们可以发现有大量的边重合,考虑把他们合并起来

具体的合并方法:

不妨对每个点建\(\sqrt n\)个额外点,设第\(i\)个点的第\(j\)个额外点为\(P_{i,j}\)。我们在\(P_{i,j}\)和\(P_{i+j,j}\)连长度为\(1\)的双向边(因为每一次跳跃的花费为\(1\))

再由所有\(P_{i,j}\)向\(i\)连长度为\(0\)的边。对于\(vi≤C\)的doge,我们就由\(xi\)向\(P_{xi,vi}\)连长度为\(0\)的边即可,很容易理解

然后剩下的,就是跑一下最短路了......我直接写了一发SPFA...

内存很坑爹啊...RE了无数次...总算是把官方数据跑过了...hack的数据...实在是无能为了了

在调试的时候,发现不应该取\(\sqrt n\),实际表明,取\(\sqrt {\frac {n}{logn}}\)附近的时候比较优

为了更达到减小内存的目的,稍微增大时间,我手动的把\(\sqrt {\frac {n}{logn}}\)减小一个常数,多次提交.....

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#include<queue>
#define LL long long
#define pii pair<int,int>
#define mp make_pair using namespace std; inline char nc(){
/*
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
*/return getchar();
} inline void read(int &x){
char c=nc();int b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
} inline void read(LL &x){
char c=nc();LL b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
} inline int read(char *s)
{
char c=nc();int len=1;
for(;!(c>='a' && c<='z');c=nc()) if (c==EOF) return 0;
for(;(c>='a' && c<='z');s[len++]=c,c=nc());
s[len++]='\0';
return len;
} inline void read(char &x){
for (x=nc();!(x>='A' && x<='Z');x=nc());
} int wt,ss[19];
inline void print(int x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {
for (wt=0;x;ss[++wt]=x%10,x/=10);
for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
} int n,m,b[30010],p[30010],f[6000010];
bool v[30010];
struct data
{
int x,y;
data(int a=0,int b=0):x(a),y(b){}
};
struct cmp
{
bool operator()(data x,data y)
{
if (x.y==y.y) return x.x>y.x;
return x.y>y.y;
}
};
vector<data> a[6000010];
const int inf=1000000000; int calc(int x,int y,int z)
{
return n+x*z+y-1;
} void write(int x)
{
if (x==inf) print(-1);else print(x);
puts("");
} int main()
{
read(n);read(m);
int x=(int)sqrt(n/log(n)+0.5);
x=max(x,0);
x-=10;x=max(x,0); //手动减小常数
for (int i=0;i<m;i++)
{
read(b[i]),read(p[i]);
if (p[i]<=x) a[b[i]].push_back(data(calc(b[i],p[i],x),0));
else
{
int s=0,j=b[i];
while (j-p[i]>=0)
s+=1,j-=p[i],a[b[i]].push_back(data(j,s));
s=0,j=b[i];
while (j+p[i]<n)
s+=1,j+=p[i],a[b[i]].push_back(data(j,s));
}
}
for (int i=0;i<n;i++)
{
for (int j=1;j<=x;j++)
{
//print(calc(i,j,x)),putchar(' '),print(calc(i+j,j,x)),puts("");
if (i+j<n) a[calc(i,j,x)].push_back(data(calc(i+j,j,x),1)),
a[calc(i+j,j,x)].push_back(data(calc(i,j,x),1));
a[calc(i,j,x)].push_back(data(i,0));
}
}
n=calc(n-1,x,x);
for (int i=0;i<=n;i++)
f[i]=inf;
f[b[0]]=0;
memset(v,0,sizeof(v));
priority_queue<data,vector<data>,cmp> q;
q.push(data(b[0],0));
while (!q.empty())
{
int x=q.top().x,y=q.top().y;q.pop();
while (y>f[x])
{
if (q.empty()) break;
x=q.top().x,y=q.top().y,q.pop();
}
if (y>f[x]) break;
for (int j=0;j<a[x].size();j++)
if (f[x]+a[x][j].y<f[a[x][j].x])
f[a[x][j].x]=f[x]+a[x][j].y,q.push(data(a[x][j].x,f[a[x][j].x]));
}
write(f[b[1]]);
return 0;
}

【APIO2015】Jakarta Skyscrapers的更多相关文章

  1. uoj #111. 【APIO2015】Jakarta Skyscrapers

    #111. [APIO2015]Jakarta Skyscrapers 印尼首都雅加达市有 NN 座摩天楼,它们排列成一条直线,我们从左到右依次将它们编号为 00 到 N−1N−1.除了这 NN 座摩 ...

  2. bzoj4069【APIO2015】巴厘岛的雕塑

    4069: [Apio2015]巴厘岛的雕塑 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 192  Solved: 89 [Submit][Stat ...

  3. 【数学建模】【APIO2015】Palembang Bridges

    Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 10000 ...

  4. UOJ#110. 【APIO2015】Bali Sculptures 贪心 动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ110.html 题解 我们发现n=2000 的子任务保证A=1! 分两种情况讨论: $n\leq 100$ ...

  5. 【BZOJ4069】【APIO2015】巴厘岛的雕塑 [贪心][DP]

    巴厘岛的雕塑 Time Limit: 10 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Description 印尼巴厘岛的公路上有许多的雕塑, ...

  6. UOJ#110. 【APIO2015】Bali Sculptures

    印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道. 在这条主干道上一共有 NN 座雕塑,为方便起见,我们把这些雕塑从 11 到 NN 连续地进行标号,其中第 ii 座雕塑的年龄是 YiYi 年 ...

  7. 【APIO2015】Palembang Bridges

    题目描述 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 $A$ 和区域 $B$. 每一块区域沿着河岸都建了恰好 $1000000001$ 栋的建筑,每条岸边的建筑都从 $0$ 编号到 $100 ...

  8. 【APIO2015】Bali Sculptures

    题目描述 印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道. 在这条主干道上一共有 $N$ 座雕塑,为方便起见,我们把这些雕塑从 $1$ 到 $N$ 连续地进行标号,其中第 $i$ 座雕塑的年 ...

  9. 【BZOJ4071】【APIO2015】巴邻旁之桥

    题意: Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1 ...

随机推荐

  1. 2018 Multi-University Training Contest 1 Balanced Sequence(贪心)

    题意: t组测试数据,每组数据有 n 个只由 '(' 和 ')' 构成的括号串. 要求把这 n 个串排序然后组成一个大的括号串,使得能够匹配的括号数最多. 如()()答案能够匹配的括号数是 4,(() ...

  2. [solution]腾讯TEG_计算广告组_算法题

    度娘笔试归来,题目实打实的,感觉真心不易,上百号人就抢那么几个坑......只恨自己平时积累太少啊~ 故曝一道鹅厂面试用的算法题(当时我就死在了这题上),来为度娘家攒一下RP~ 题目: 对于长度为N的 ...

  3. HDU 3473 Minimum Sum 划分树

    题意: 给出一个长度为\(n(1 \leq n \leq 10^5)\)的序列\(a\) 有若干次查询l r:找到一个\(x\)使得\(\sum \limits_{l \leq i \leq r} \ ...

  4. Service 回顾

    绑定本地service需要实现onBind()方法

  5. luogu2774 方格取数问题

    最大点权独立集,参见胡伯涛论文 #include <iostream> #include <cstring> #include <cstdio> #include ...

  6. Install Oracle 11G Release 2 (11.2) on Centos Linux 7

    Install Oracle 11G Release 2 (11.2) on Centos Linux 7 This article presents how to install Oracle 11 ...

  7. 【JavaScript】关于 setInterval() 调用函数方法的一次实验

    实验主题: setInterval() 方法是 JS 中比较常用的一个方法.setInterval() 方法可以按照指定的周期 ( 毫秒 ) 来调用函数方法或计算表达式. setInterval() ...

  8. AtCoder Regular Contest 064 F - Rotated Palindromes

    Problem Statement Takahashi and Aoki are going to together construct a sequence of integers. First, ...

  9. bootstrap 中dropmenu不起作用

    今天在使用bootstrap发现dropmenu一直不起作用,代码是从官网拷贝过来. 网上查找可以用的页面进行一点点的去除分析,发现竟然是顺序反了导致的. 在使用dropmenu时需要引入jquery ...

  10. 用meta标签让网页用360打开时默认为极速模式

    最近做项目,用360浏览器访问自己的本地网页,发现都是默认在兼容模式下打开,做的淡入淡出轮播效果在兼容模式下看时,感觉切换很生硬.百度,发现360官网帮助里有说明用meta标签控制浏览器内核,网址为h ...