2023-CSPJ
T1. 小苹果
考虑第一问,注意到每次去走一些数字后会变成另一个子问题,且 n ′ ≤ 2 3 n n'\leq \frac{2}{3}n n′≤32n,故暴力维护即可。
对于第二问,用同样的办法,暴力维护 n n n 的标号即可。
Code 100pts
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -f;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
int main()
{
// freopen("in.txt", "r", stdin); //输入重定向,目录下的in.txt文件中读取
// freopen("out.txt", "w", stdout); //输出重定向,目录下的out.txt文件中
int n = read();
int cnt = 0, res = 0;
while (n)
{
cnt++;
int k = (n - 1) % 3;
if (res == 0 && k == 0)
res = cnt;
n = n - 1 - (n - 1) / 3; // 每次减少的数量。
}
cout << cnt << ' ' << res;
return 0;
}
T2.公路
考虑贪心,对于每个加油站,找到下一个比他便宜的油站,每次在这个位置加尽可能少的油,使得能顺利开到到下一个比他便宜的油站,一路贪心下去就可以了。注意开long long
拓展一下: 题目中的车的油桶是无限的。 如果现限制油桶容积这需要考虑新的算法。 用单调栈。
Code 100pts
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int n, d;
LL v[N], a[N]; // v[i] 代表i-i+1 的距离, a[i] 代表第i个站点的油价a[i]
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -f;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
int main()
{
// freopen("in.txt", "r", stdin); //输入重定向,目录下的in.txt文件中读取
// freopen("out.txt", "w", stdout); //输出重定向,目录下的out.txt文件中
n = read(), d = read();
for (int i = 1; i < n; i++)
cin >> v[i];
for (int i = 1; i <= n; i++)
cin >> a[i];
LL ans = 0;
LL best = 1e6;
LL dis = 0;
for (int i = 1; i <= n; i++)
{
best = min(a[i], best); // 选择最优的油价前往下一个目的地。
dis += v[i]; // 下个目的地还需要加多少油,因为前面一次加油肯定有剩下油。
LL oil = (dis + d - 1) / d; //确认下这次加油数量,上取整计算。
ans += (dis + d - 1) / d * best; //更新下答案。
dis -= oil * d; // 注意我们需要更新下dis,
// cout << best << ' ' << ans << endl; //
}
cout << ans << endl;
return 0;
}
T3. 一元二次方程
根据题意模拟即可。取得50分比较容易。即符合条件C,但是拿满分比较难,需要注意比较多的细节代码。
Code 50pts
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int t, m;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -f;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
void solve(int a, int b, int c)
{
int delta = b * b - 4 * a * c;
// 无根的情况先处理。
if (delta < 0)
{
cout << "NO" << endl;
return;
}
// 剩下有根的情况需要讨论啦。
// 首先把a>0 的情况和a<0 的情况分开讨论。
if (a > 0)
{
int res = (-b + (int)sqrt(delta)) / (2 * a);
cout << res << endl;
return;
}
else
{
int res = (-b - (int)sqrt(delta)) / (2 * a);
cout << res << endl;
return;
}
}
int main()
{
// freopen("in.txt", "r", stdin); //输入重定向,目录下的in.txt文件中读取
// freopen("out.txt", "w", stdout); //输出重定向,目录下的out.txt文件中
t = read(), m = read();
while (t--)
{
int a = read(), b = read(), c = read();
solve(a, b, c);
}
return 0;
}
Code 100pts
#include <bits/stdc++.h>
using namespace std;
// 输出一个有理数
void print(int fz, int fm)
{
int g = abs(__gcd(fz, fm));
fz /= g, fm /= g;
if (fm == 1)
printf("%d", fz);
else
printf("%d/%d", fz, fm);
}
int main()
{
int _;
scanf("%d%*d", &_);
while (_--)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
int d = b * b - 4 * a * c;
if (d < 0)
printf("NO");
else
{
int x = 0;
for (int i = 1; i * i <= d; i++)
{
if (d % (i * i) == 0)
x = i;
}
if (x * x == d)
{
int fz, fm;
if (a > 0)
fz = -b + x, fm = 2 * a;
else
fz = b + x, fm = -2 * a;
print(fz, fm);
}
else
{
int fz, fm;
if (a > 0)
fz = -b, fm = 2 * a;
else
fz = b, fm = -2 * a;
if (fz != 0)
{
print(fz, fm);
putchar('+');
}
int r = d / (x * x);
if (a > 0)
fz = x, fm = 2 * a;
else
fz = x, fm = -2 * a;
int g = __gcd(fz, fm);
fz /= g, fm /= g;
if (fm == 1)
{
if (fz == 1)
printf("sqrt(%d)", r);
else
printf("%d*sqrt(%d)", fz, r);
}
else if (fz == 1)
{
printf("sqrt(%d)/%d", r, fm);
}
else
{
printf("%d*sqrt(%d)/%d", fz, r, fm);
}
}
}
puts("");
}
return 0;
}
T4. 旅游巴士
考虑拆点,把一个点 u u u 拆成 ( u , 0 ∼ k − 1 ) (u,0\sim k-1) (u,0∼k−1),若原图存在一条 u → v u\to v u→v 的边,那么新图上则有 ( u , x ) → ( v , ( x + 1 ) m o d k ) (u,x)\to (v,(x+1)\bmod k) (u,x)→(v,(x+1)modk)。
那么问题转化成求 ( 1 , 0 ) → ( n , 0 ) (1,0)\to (n,0) (1,0)→(n,0) 的最短路。
考虑边的限制怎么解决,注意到我们可以任选开局的时机,因此如果在一个位置被卡住了,可以通过不断加 k k k 直到满足边的时间限制,走回头路一定不优,这个过程用一次 dij 实现即可实现。
时间复杂度 O ( n k log ( m k ) ) \mathcal{O}(nk\log (mk)) O(nklog(mk))。
此外,注意到到达时间越晚,关于边的限制也就越松,因此可以拆点后二分到达时间然后倒着 bfs。
Code 100pts
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 10005;
vector<pair<int, int>> G[N];
int d[N][105];
int vis[N][105];
struct Node
{
int u, i, d;
bool operator<(const Node &rhs) const
{
return d > rhs.d;
}
};
int main()
{
int n, m, kk;
scanf("%d%d%d", &n, &m, &kk);
while (m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
G[u].push_back({v, w});
}
memset(d, 0x3f, sizeof d);
priority_queue<Node> q;
q.push({1, 0, d[1][0] = 0});
while (q.size())
{
int u = q.top().u, i = q.top().i;
q.pop();
if (vis[u][i])
continue;
vis[u][i] = 1;
for (auto [v, w] : G[u])
{
int t = d[u][i], j = (i + 1) % kk;
if (t < w)
t += (w - t + kk - 1) / kk * kk;
if (d[v][j] > t + 1)
q.push({v, j, d[v][j] = t + 1});
}
}
if (d[n][0] == INF)
d[n][0] = -1;
printf("%d\n", d[n][0]);
return 0;
}