逐月信息学 2024 提高组 #6
\(\color{black}\texttt{A. 数字涡旋}\)
题目描述
有一张无线大的表格,里面填着所有正整数,表格如下:
求数字 \(N\) 出现在表格的几行几列。
思路
推式子体。
空间复杂度 \(O(1)\),时间复杂度 \(O(T)\)。
代码
#include<bits/stdc++.h>
using namespace std;
int t, n, k;
int Sqrt(int x) {
int l = 1, r = 31624;
for(; l < r; ) {
int mid = (l + r) >> 1;
(1ll * mid * mid >= x ? r = mid : l = mid + 1);
}
return l;
}
void Solve() {
cin >> n;
k = Sqrt(n);
int d = k * k - n;
if(k % 2 == 0) {
if(d < k) {
cout << k << " " << 1 + d << "\n";
}else {
cout << k - (d - k + 1) << " " << k << "\n";
}
}else {
if(d < k) {
cout << 1 + d << " " << k << "\n";
}else {
cout << k << " " << k - (d - k + 1) << "\n";
}
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> t; t--; Solve()) {
}
return 0;
}
\(\color{black}\texttt{B. 潦草急就}\)
题目描述
求 \(0\) 到 \(N\) 之间在 \(K\) 进制下数字 \(0\) 到 \(K-1\) 分别出现了多少次(不包括前导零)。
思路
还是找规律,但是更难了些。
按位枚举即可。也可以用差分优化。
空间复杂度 \(O(K)\),时间复杂度 \(O(T(K+\log_K N))\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXK = 17, MOD = 2027;
int t, k;
ll n, ans[MAXK];
void Solve() {
cin >> n >> k;
for(int i = 0; i < k; ++i) {
ans[i] = 0;
}
n++;
for(ll i = 1; i <= n; i *= k) {
ans[0] = (ans[0] + 1ll * n / (i * k) * i);
ans[0] = (ans[0] + i);
ans[n % (i * k) / i] = (ans[n % (i * k) / i] - i + n % (i * k) % i);
ans[n % (i * k) / i + 1] -= n % (i * k) % i;
ans[0] = (ans[0] - min(n, i));
ans[1] = (ans[1] + min(n, i));
}
cout << (ans[0] + 1) % MOD << " ";
for(int i = 1; i < k; ++i) {
ans[i] = (ans[i] + ans[i - 1]);
cout << ans[i] % MOD << " \n"[i == k - 1];
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> t; t--; Solve()) {
}
return 0;
}
\(\color{black}\texttt{C. 柳暗花明}\)
题目描述
给定一个序列 \(A\) 和 \(Q\) 次询问,令 \(f_x(l,r)=\begin{cases}x&l>r\\\lfloor\frac{f_x(l,r-1)+A_r}{2}\rfloor&l\le r\end{cases}\),求对于每次询问求 \(f_x(l,r)\) 的值。
思路
首先看这些数对答案的贡献:假设选择 \([l,r],x\),则 \(f_x(l,r)\le \frac{\frac{\frac{\frac{x+A_l}{2}+A_{l+1}}{2}+\dots}{2}+A_r}{2}=\frac{x}{2^{r-l+1}}+\frac{A_l}{2^{r-l+1}}+\frac{A_{l+1}}{2^{r-l}}+\dots+\frac{A_r}{2}\),可以发现,前面很多的部分对答案的贡献微乎其微,向下取整后直接消失,所以实际上只需计算后 \(100\) 个数即可。
空间复杂度 \(O(N)\),时间复杂度 \(O(Q)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200001;
int n, a[MAXN], q;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
}
cin >> q;
for(int i = 1, x, l, r; i <= q; ++i) {
cin >> x >> l >> r;
for(int j = max(l, r - 100); j <= r; ++j) {
x = (x + a[j]) / 2;
}
cout << x << "\n";
}
return 0;
}
\(\color{black}\texttt{D. 移动硬币}\)
题目描述
有 \(N\) 种硬币,每种硬币的面额为 \(w_i\),并且每种硬币都有 \(1145141919810!^{1145141919810!^{1145141919810!^{\dots^{1145141919810!}}}}\}1145141919810!\) 个,求能凑出多少种不同且 \(\le L\) 的钱数。
思路
首先对 \(w\) 排序。考虑能凑出最小的钱数 \(x\) 使 \(x\equiv i\pmod {w_n}(0\le i< w_n)\),只要凑出这些钱,那么不断再用 \(w_n\) 即可。这些数 \(\mod w_n\) 不同,值也必定不同。而求最少钱数可以用同余最短路解决。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 1000001;
const ll INF = LLONG_MAX;
struct Node {
int x;
ll dis;
};
struct cmp {
bool operator()(const Node &a, const Node &b) const {
return a.dis > b.dis;
}
};
int n, w[MAXN];
ll dist[MAXN];
bool vis[MAXN];
ll l, ans;
priority_queue<Node, vector<Node>, cmp> pq;
void Record(int u, ll dis) {
if(dis >= dist[u]) {
return;
}
dist[u] = dis;
pq.push({u, dis});
}
void dij() {
fill(dist, dist + w[1], INF);
Record(0, 0);
for(; !pq.empty(); ) {
auto [u, dis] = pq.top();
pq.pop();
if(vis[u]) {
continue;
}
vis[u] = 1;
for(int i = 2; i <= n; ++i) {
Record((u + w[i]) % w[1], dis + w[i]);
}
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> l;
for(int i = 1; i <= n; ++i) {
cin >> w[i];
}
dij();
for(int i = 0; i < min(l, 0ll + w[1]); ++i) {
if(dist[i] <= l) {
ans += (l - dist[i]) / w[1] + 1;
}
}
cout << ans - 1;
return 0;
}