休息了一天~
H
题意:
初始有
k
=
0
k=0
k=0根香蕉,有两种操作可以使得香蕉的个数
k
k
k变化。
t
=
1
t=1
t=1时,一次时间戳内可以进行
[
0
,
y
]
[0,y]
[0,y]次
k
=
k
+
x
k=k+x
k=k+x的操作,
t
=
2
t=2
t=2时,一次时间戳内可以进行
[
0
,
y
]
[0,y]
[0,y]次
k
=
k
×
x
k=k\times x
k=k×x的操作。问获得
[
1
,
m
]
[1,m]
[1,m]根香蕉的时间戳最小分别是多少,如果某个数量的香蕉不能获得,则输出
−
1
-1
−1。
数据范围:
1
≤
n
≤
200
,
1
≤
m
≤
1
0
5
,
1
≤
t
≤
2
,
0
≤
y
≤
1
0
5
,
x
=
⌈
x
′
1
0
5
⌉
1\leq n\leq 200,1\leq m\leq 10^5,1\leq t\leq 2,0\leq y\leq 10^5,x=\lceil \frac{x'}{10^5} \rceil
1≤n≤200,1≤m≤105,1≤t≤2,0≤y≤105,x=⌈105x′⌉。
当
t
=
1
t=1
t=1,
1
≤
x
′
≤
1
0
5
1\leq x'\leq 10^5
1≤x′≤105;当
t
=
2
t=2
t=2,
1
≤
x
′
≤
1
0
5
×
m
1\leq x'\leq 10^5\times m
1≤x′≤105×m
题解:
暴力的想法即模拟,时间复杂度为:
O
(
n
m
2
)
O(nm^2)
O(nm2)
考虑优化:
可以知道的是,我们在时间戳
i
i
i枚举
t
i
m
e
[
j
]
time[j]
time[j]作为更新之后状态的时间戳必须是
t
i
m
e
[
j
]
time[j]
time[j]可以到达。同时,我们从后向前枚举,保证新更新的时间戳都是由之前的状态更新而来的,类似
01
01
01背包优化空间复杂度的思想。当
t
i
m
e
[
j
+
c
n
t
×
x
]
time[j+cnt\times x]
time[j+cnt×x]已经存在了,那么可以知道的是从
t
i
m
e
[
j
+
c
n
t
×
x
]
time[j+cnt\times x]
time[j+cnt×x]开始枚举之后的状态的时间戳一定会比
t
i
m
e
[
j
]
time[j]
time[j]更远,所以如果
t
i
m
e
[
j
+
c
n
t
×
x
]
time[j+cnt\times x]
time[j+cnt×x]已经有了则无需继续枚举。乘法同理。
分析下时间复杂度:
枚举
n
n
n次操作和每次枚举
[
0
,
m
]
[0,m]
[0,m]这
m
+
1
m+1
m+1个状态共
n
×
(
m
+
1
)
n\times (m+1)
n×(m+1)次
此外每次只会枚举所有的已有时间戳的状态作为起点,同时每个点最多会被时间戳占用一次,因此总共最多占用
m
m
m次,因此
n
n
n次枚举最多只会使得
m
m
m个点每次被访问一次,因此总共上限是
n
(
2
m
+
1
)
n(2m+1)
n(2m+1)次,即
O
(
n
m
)
O(nm)
O(nm)的时间复杂度。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 1e5 + 10;
int ans[M];
int n, m;
ll t, x, y;
int main()
{
scanf("%d%d", &n, &m);
memset(ans, -1, sizeof ans);
ans[0] = 0;
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld%lld", &t, &x, &y);
if(t == 1) x = (x + 99999) / 100000;
for(int j = m; j >= 0; --j) {
if(ans[j] == -1) continue;
ll z = j;
for(int k = 0; k < y; ++k) {
if(t == 1) z += x;
else z = (z * x + 99999) / 100000;
if(z > m) break;
if(ans[z] != -1) break;
ans[z] = i;
}
}
}
for(int i = 1; i <= m; ++i) printf("%d%c", ans[i], (i == m) ? '\n' : ' ');
return 0;
}