【题意】 你一开始在第一层。一共有
h
h
h 层。 你每一次操作,可以向上
x
x
x 层,或向上
y
y
y 层,或向上
z
z
z 层,或回到第一层。 问你一共有多少层楼是你可以到达的。
【范围】
1
≤
h
≤
2
63
−
1
1\le h\le 2^{63}-1
1≤h≤263−1
1
≤
x
,
y
,
z
≤
1
0
5
1\le x,y,z\le 10^5
1≤x,y,z≤105
思路一:动态规划
设
d
p
[
x
]
dp[x]
dp[x] ,其中
d
p
[
x
]
=
1
dp[x]=1
dp[x]=1 表示能到达这一层楼,否则表示不能到达这一层楼。 我们得到简单的状态转移方程:
d
p
[
t
]
=
d
p
[
t
−
x
]
∣
d
p
[
t
−
y
]
∣
d
p
[
t
−
z
]
dp[t]=dp[t-x]\ |\ dp[t-y] \ |\ dp[t-z]
dp[t]=dp[t−x]∣dp[t−y]∣dp[t−z] 简单的初始化:
d
p
[
1
]
=
1
,
d
p
[
t
<
1
]
=
0
dp[1]=1,dp[t<1]=0
dp[1]=1,dp[t<1]=0,这里下标记作可以为负。
但是由于我们的
h
h
h 达到了很大的数量级,故这种做法时间复杂度为
O
(
h
)
O(h)
O(h),只能得到
T
L
E
\color{red}TLE
TLE
⌈
\lceil
⌈同余最短路
⌋
\rfloor
⌋
考虑到,如果我们通过某些操作到达了第
i
i
i 层, 那么我们可以到达
i
+
a
x
i+ax
i+ax 层,其中
a
∈
N
a\in \mathbb{N}
a∈N 那么,如果我们能到达
i
i
i 层,这一层及上面还可以到达的层数为
⌊
h
−
i
x
⌋
+
1
\lfloor\dfrac{h-i}{x}\rfloor+1
⌊xh−i⌋+1 层。 容易得到,如果能到达
i
+
a
x
i+ax
i+ax 层,其中
a
∈
N
a\in \mathbb{N}
a∈N,那么必然能到达
i
i
i 层。 不妨,我们求出最小的一些
i
i
i,使得
i
i
i 层为不用操作
x
x
x 就能到达的最小楼层。 从同余的角度分析,即求出每一个
i
∈
[
0
,
a
)
i\in[0,a)
i∈[0,a),算出到达
t
%
a
=
i
t\%a=i
t%a=i 层的最小的
t
t
t。
我们设
d
p
[
i
]
dp[i]
dp[i] 表示
min
t
{
t
%
a
=
i
满
足
t
=
b
y
+
c
z
,
其
中
b
,
c
∈
N
}
\min_t\{t\%a=i\quad 满足\quad t=by+cz,其中 b,c\in \mathbb{N}\}
mint{t%a=i满足t=by+cz,其中b,c∈N} 式子按照数学的角度设出来比较合理,但是做起来比较复杂。此时我们要按照图论的角度去分析了。 如果我们能到达
i
i
i 层,那么我们还能到达
i
+
y
i+y
i+y 层和
i
+
z
i+z
i+z 层,当然前提是层数
≤
h
\le h
≤h 按照
d
p
dp
dp 式子建图,即
i
→
边
权
为
y
(
i
+
y
)
%
a
i\overset{边权为y}{\rightarrow}(i+y)\%a
i→边权为y(i+y)%a 和
i
→
边
权
为
z
(
i
+
z
)
%
a
i\overset{边权为z}{\rightarrow}(i+z)\%a
i→边权为z(i+z)%a 容易得到这就是跑一个最短路。点数和边数都是
1
e
5
1e5
1e5 级别的,用
d
i
j
k
s
t
r
a
dijkstra
dijkstra 即可。
核心代码
A
c
c
e
p
t
e
d
\color{green}Accepted
Accepted
时间复杂度:
O
(
a
log
a
)
O(a\log a)
O(aloga)
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
ll h;int a,b,c;
ll dp[MAX];
vector<int>V[MAX],G[MAX];
priority_queue<pair<ll,ll>>Q;voiddijkstra(int s){
Q.push(make_pair(-1,s));while(!Q.empty()){int x = Q.top().second;
ll t =-Q.top().first;
Q.pop();if(t > dp[x])continue;for(auto it : V[x]){if(dp[it]> dp[x]+ a && dp[x]+ a <= h){
dp[it]= dp[x]+ a;
Q.push(make_pair(-dp[it],it));}}for(auto it : G[x]){if(dp[it]> dp[x]+ b && dp[x]+ b <= h){
dp[it]= dp[x]+ b;
Q.push(make_pair(-dp[it],it));}}}}intmain(){
cin >> h >> a >> b >> c;for(int i =0;i < c;++i)dp[i]= LINF;
dp[1]=1;for(int i =0;i <= c;++i){
V[i].push_back((i+a)%c);
G[i].push_back((i+b)%c);}dijkstra(1);
ll res =0;for(int i =0;i < c;++i){if(dp[i]== LINF)continue;
res = res +((h - dp[i])/ c)+1;}
cout << res;return0;}
【题意】 给定
n
,
a
1
,
⋯
,
a
n
,
l
,
r
n,a_1,\cdots,a_n,l,r
n,a1,⋯,an,l,r 问你有多少组非负解向量
x
1
,
⋯
,
x
n
x_1,\cdots,x_n
x1,⋯,xn,满足:
∑
i
=
1
n
a
i
x
i
∈
[
l
,
r
]
\sum_{i=1}^na_ix_i\in[l,r]
i=1∑naixi∈[l,r]
【范围】
n
≤
12
n\le 12
n≤12
0
≤
a
i
≤
5
×
1
0
5
0\le a_i\le 5\times 10^5
0≤ai≤5×105
1
≤
l
≤
r
≤
1
0
12
1\le l\le r\le 10^{12}
1≤l≤r≤1012
【思路】 首先和第一题的跳楼机非常非常像。跳楼机的每种操作次数相当于这里的解向量的元素
x
i
x_i
xi的值。 为了方便,设
d
p
[
i
]
dp[i]
dp[i] 表示
min
h
{
h
%
a
1
=
i
并
且
h
可
以
到
达
}
\min_h\{h\%a_1=i\quad并且\quad h可以到达\}
minh{h%a1=i并且h可以到达} 然后正常地去建图貌似就没区别了。 不过它让你求
[
l
,
r
]
[l,r]
[l,r] 范围内解的数量,我们只要求出
[
1
,
r
]
[1,r]
[1,r] 数量减去
[
1
,
l
−
1
]
[1,l-1]
[1,l−1] 数量即可。
【代码】
A
c
c
e
p
t
e
d
\color{green}Accepted
Accepted 时间复杂度:
O
(
n
×
a
1
log
a
1
)
O(n\times a_1\log a_1)
O(n×a1loga1)
【思路】 设
w
=
min
(
d
1
,
2
,
d
2
,
3
)
w=\min(d_{1,2},d_{2,3})
w=min(d1,2,d2,3)。 如果你跑到
2
2
2 号点已经走了
x
x
x 距离的话,你可以通过来回跳跃,每次增加
2
w
2w
2w 的方式到达
2
2
2 号点。 于是,此时最短路长度为:
x
+
2
w
t
≥
K
x+2wt\ge K
x+2wt≥K,满足
t
≥
0
t\ge0
t≥0 就跟跳楼机类似。 我们只需要求出
i
∈
[
0
,
2
w
)
i\in[0,2w)
i∈[0,2w),到每个节点
v
v
v 的路径长模
i
i
i 的最小值
d
p
[
x
]
[
i
]
dp[x][i]
dp[x][i] 即可。 然后我们需要求得所有的
d
p
[
2
]
[
i
]
dp[2][i]
dp[2][i],然后求出最小的
t
≥
0
t\ge0
t≥0 满足
x
+
2
w
t
≥
K
x+2wt\ge K
x+2wt≥K 那么答案即为
min
{
x
+
2
w
t
}
\min\{x+2wt\}
min{x+2wt}
一些小细节: 虽然我们对于每一个点都有
2
w
2w
2w 个状态,那么我们优先队列是不是需要存
{
x
,
i
,
d
p
[
x
]
[
i
]
}
\{x,i,dp[x][i]\}
{x,i,dp[x][i]} 呢?这里不需要,因为根据定义得到
d
p
[
x
]
[
i
]
%
2
w
=
i
dp[x][i]\%2w=i
dp[x][i]%2w=i,我们少存一维状态,可以节省许多时间和内存,防止代码超时。
【代码】
A
c
c
e
p
t
e
d
\color{green}Accepted
Accepted 时间复杂度:
O
(
d
log
d
)
O(d\log d)
O(dlogd)
/*
_ __ __ _ _
| | \ \ / / | | (_)
| |__ _ _ \ V /__ _ _ __ | | ___ _
| '_ \| | | | \ // _` | '_ \| | / _ \ |
| |_) | |_| | | | (_| | | | | |___| __/ |
|_.__/ \__, | \_/\__,_|_| |_\_____/\___|_|
__/ |
|___/
*/
ll K;int aa[5][5];
ll dp[10][MAX];struct node{int x;
ll shu;
bool operator <(const node &ND)const{return shu > ND.shu;}};
priority_queue<node>Q;
vector<pair<int,int>>V[5];voiddijkstra(int s,ll w){
Q.push({s,0});while(!Q.empty()){int x = Q.top().x;int y = Q.top().shu % w;
ll t = Q.top().shu;
Q.pop();if(t > dp[x][y])continue;for(auto it : V[x]){int to = it.first;int d = it.second;if(dp[to][(t + d)% w]> t + d){
dp[to][(t + d)% w]= t + d;
Q.push({to,dp[to][(t + d)% w]});}}}}
ll solve(){
ll w =min(aa[1][2],aa[2][3]);
w = w *2;if(w >= K)return w;for(int i =1;i <=4;++i)for(int j =0;j < w;++j)
dp[i][j]= LINF;
dp[2][0]=0;dijkstra(2,w);
ll res = LINF;for(int i =0;i < w;++i){if(dp[2][i]== LINF)continue;if(dp[2][i]>= K){/// 我们不能允许 t 为负数
res =min(res,dp[2][i]);continue;}/// 简单使用 floor 函数会导致精度出现错误
res =min(res,dp[2][i]+ w *(((K - dp[2][i])/ w)+((K - dp[2][i])% w !=0?1LL:0LL)));}return res;}intmain(){int T;cin >> T;while(T--){
cin >> K >> aa[1][2]>> aa[2][3]>> aa[3][4]>> aa[4][1];
aa[2][1]= aa[1][2];
aa[3][2]= aa[2][3];
aa[4][3]= aa[3][4];
aa[1][4]= aa[4][1];for(int i =1;i <=4;++i)V[i].clear();for(int i =1;i <=4;++i){int to = i %4+1;
V[i].push_back(make_pair(to,aa[i][to]));
V[to].push_back(make_pair(i,aa[i][to]));}
cout <<solve()<< endl;}return0;}