暑期集训赛01

· · 个人记录

第一套

回文立方数 (cube)

当然,让我帮你翻译:

只有 O(N^{\frac{1}{3}}) 个小于或等于 N 的立方数。因此,可以对所有立方数 N 进行暴力搜索,并检查其十进制表示是否是回文。

出题人良心的馈赠。

#include <bits/stdc++.h>
using namespace std;

#define int long long

int n;

namespace GTR {
    const int bufl = 1 << 15;
    char buf[bufl], *s = buf, *t = buf;
    inline int fetch() {
        if (s == t) { t = (s = buf) + fread(buf, 1, bufl, stdin); if (s == t) return EOF; }
        return *s++;
    }
    inline int read() {
        int a = 0, b = 1, c = fetch();
        while (c < 48 || c > 57) b ^= c == '-', c = fetch();
        while (c >= 48 && c <= 57) a = (a << 1) + (a << 3) + c - 48, c = fetch();
        return b ? a : -a;
    }
} using GTR::read;

int judge(int x) {
    int p[50] = {0};
    int m = 1;
    for ( ; x != 0; ++ m, x /= 10) p[m] = x % 10;
    -- m;
    for (int i = 1, j = m; i <= m / 2; ++ i, -- j) {
        if (p[i] != p[j]) {
            return 0;
        }
    }
    return 1;
}

signed main() {
    n = read();
    int ans = 0;
    for (int i = 0; i * i * i <= n; ++ i) {
        int x = i * i * i;
        if (judge(x)) {
            ans = x;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

数学小店的奇妙兑换 (drink.cpp)

通过观察本题可以得知答案是 \frac{n}{k-1},由于数字较大,我们可以使用一个大整数除法的模板来解决本题。

这个问题的本质是学习除法。在原始场景中(10 个饮料瓶,每 3 个瓶子换一瓶饮料,最终能喝 5 瓶饮料),我们可以看到,10 个饮料瓶最终喝了 5 瓶饮料。根据除法的定义:总价除以数量等于单价,得到每瓶饮料的单价是 \frac{10}{5}=2。得到单价之后,根据除法的定义继续:总价除以单价等于数量,也就是说假设有 n 元,则可以喝 \frac{n}{2} 瓶饮料。

推广到每 k 个瓶子换一瓶饮料,可以简单地发现答案变为 \frac{n}{k-1}

如果还需要证明的话,我们可以根据方程的思想来解决这个问题:

两边同时减 1,解得 1 瓶饮料 = $k-1$ 个空瓶子 ```cpp int main() { #define int long long int p = 0, q = 0, k = 0, n, j = 0, flag = 0; scanf("%s %d", a, &q); q--; n = strlen(a); for(int i = 0; i < n; i++){ p = p * 10 + a[i] - '0'; if(p >= q) { k = p / q; ans[j++] = k + '0'; p = p % q; } else ans[j++] = '0'; } for(int i = 0; i < j; i++) { if(ans[i] != '0') flag = 1; if(flag == 1) printf("%c", ans[i]); } } ``` ## 烧烤(bbq.cpp) 首先暴力肯定是没问题,出题人的良心已经体现在这额外的 $10\%$ 的分数中了。 下面直接介绍正解。 1. **回文判断**:首先通过哈希或者 manacher 快速判断起始字符串是否为回文串。如果是回文串,那么先手玩家立即输掉比赛。 2. **局面分析**:对于任意一个局面,若先手无法进行任何操作,则说明无论删去开头还是结尾都会得到回文串。我们可以发现符合条件的字符串只能形如 `ab`, `abab`, `ababab` 等,这说明终止状态的长度一定是偶数。因此,**输赢只和起始字符串长度的奇偶性有关。** 3. **时间复杂度**:该方法的时间复杂度为 $O(n + q)$,其中 $n$ 是字符串的长度,$q$ 是查询的数量。 ```cpp int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int n,q; string s; cin>>n>>q>>s; vector<int> d1(n); for(int i = 0, l = 0,r = -1; i < n; i++){ int k = (i > r) ? 1 : min(d1[l + r - i],r - i + 1); while(0 <= i - k && i + k < n && s[i - k] == s[i + k]) k++; d1[i] = k--; if(i + k > r){ l = i - k; r = i + k; } } while(q--){ cin>>l>>r; l--,r--; int mid = (l + r)/2; if((r - l + 1) % 2 == 1 ) { if(r - l + 1 <= 2 * d1[mid] - 1) cout<<"Cow\n"; else cout<<"Guan\n"; } else cout<<"Cow\n"; } return 0; } ``` ## 神奇树 (growing) 对于这些操作来说,每一个点都有一个加入的时刻,以及每个第二类操作也有一个时刻。 每一个点会被哪些二类操作影响权值呢?也就是那些对其祖先(或自己)的,且操作时间都比这个点加入时间晚的那些操作才会被影响。 所以,我们可以维护每一个点对应的操作序列,以及当前对应的二类操作集合。对于最终形态的树,我们从根节点开始 dfs,遇到一个点之后就将其对应的二类操作全部加入集合内,然后再查询对于这个点的答案,也就是当前集合内时间大于该节点被加入时间对应的二类操作的 x 之和。然后在 dfs 回溯的时候,把这些二类操作全部从集合内删除。 上述这个集合维护的内容其实是:大于某个时间的 x 之和。这个东西可以用线段树或树状数组维护。用树状数组的常数会比线段树小得多。 时间复杂度:$O(Q \log N)$,其中 $N$ 是树的点数。 ``` #include <bits/stdc++.h> using namespace std; using ll = long long; using pii = pair<int, int>; const int N = 5e5 + 50; const int mod = 1e9 + 7; int n, m; struct fenwick { ll t[500050]; ll n; int lowbit(int x) { return x & (-x); } void update(int x, ll v) { while (x <= n) t[x] += v, x += lowbit(x); } ll query(ll x) { ll ret = 0; while (x) ret += t[x], x -= lowbit(x); return ret; } ll query(int l, int r) { if (l > r) return 0; return query(r) - query(l - 1); } void clear() { fill(t, t + n + 5, 0); } } t; vector<int> e[N]; int tim[N]; vector<pii> q[N]; ll a[N]; void dfs(int u) { a[u] = 0; for (auto [ti, xi] : q[u]) t.update(ti, xi); a[u] = t.query(tim[u], t.n); for (auto v : e[u]) dfs(v); for (auto [ti, xi] : q[u]) t.update(ti, -xi); } void solve() { int Q; cin >> Q; t.n = Q + 1; m = 1; tim[1] = 1; for (int i = 1; i <= Q + 5; i++) q[i].clear(), e[i].clear(); for (int i = 2; i <= Q + 1; i++) { int ti, vi, xi; cin >> ti >> vi; if (ti == 1) { m++; e[vi].push_back(m); tim[m] = i; } else { cin >> xi; q[vi].push_back({i, xi}); } } dfs(1); for (int i = 1; i <= m; i++) cout << a[i] << " \n"[i == m]; t.clear(); } int main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T = 1; cin >> T; while (T--) { solve(); } return 0; } ```