11.4训练赛

A.P1066 2^k进制数

dp[i][j]表示最高位在i取j时的方案数,需要高精度,这里用py并滚动掉第一维逆序迭代

k, w = map(int, input().split())
l = pow(2, k) - 1
n = min((w + k - 1) // k, l)  # 最长位数
m = min(pow(2, w - (n - 1) * k) - 1, l - n + 1)  # 最高位上限

dp = [1] * l
dp[0], ans = 0, 0

for i in range(n - 1):  # 直接从倒数第二位开始
    for j in range(1, l - i):  # 逆序
        dp[j] += dp[j - 1]
        if i < n - 2:
            ans += dp[j]
for i in range(m):
    ans += dp[l - n + 1 - i]
print(ans)

用组合数学考虑\(k \mid w\)\(k \nmid w\)的情况得到答案为 \(\sum_{i = 2}^j C_l^i + \sum_{i = 1}^{2^{n \mod k} - 1} C_{l - i}^j\) 其中 \(j = \lfloor w / k \rfloor,\ l = 2^k - 1\)

从dp的转移与组合数递推的相似之处也可推出答案。

B.P1099 树网的核

这题可以直接暴力,如果要降低复杂度需要对树的直径的性质有一定了解。

首先注意到答案的下界为直径的偏心距(长度足够取整条直径时),记为D,
取不到时考虑其它点对路径偏心距的贡献,分成两部分,一部分是从路径某点出发不经过直径到达的点,
不会超过D,可以忽略,另一部分的点贡献显然是路径端点到直径端点的最大距离(如果还有更大的就与直径定义矛盾了)

所以我们在直径上尺取长度合法的路径,记路径端点为p1,p2,直径端点为d1,d2,
则路径相对于直径的偏心距为 \(max(dis(p1, d1), dis(p2, d2))\),最小化这个值,记为d,则答案为\(max(d,D)\)

如果有多条直径,考虑其中任意两条,必然相交于树上若干个点(如不相交则可以通过
连接这两条直径的边构造出更长的路径,矛盾),这若干个点可以缩成一个点,剩下的
部分关于此点对称,所以d和D都不会变,任取一条操作即可。

理解了这种做法可以过掉这题加强版P2491

#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
const ll N = 310;
ll n, m, u, v, w, k, e, d1, d2 = 1e9;
ll fa[N], d[N], l[N];
vector<pll> g[N];

void dfs(ll u, ll f) {
    fa[u] = f, k = d[u] > d[k] ? u : k;
    for (auto [v, w] : g[u])
        if (v ^ f && !l[v])
            d[v] = d[u] + w, dfs(v, u);
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m;
    fu(i, 2, n) {
        cin >> u >> v >> w;
        g[u].emplace_back(v, w);
        g[v].emplace_back(u, w);
    }
    dfs(1, 0), d[k] = 0, dfs(k, 0), e = k;
    for (ll i = k, j = k; i; j = fa[j])
        while (i && d[j] - d[i] <= m) {
            mn(d2, max(d[e] - d[j], d[i]));
            l[i] = 1, k = i, dfs(i, fa[i]);
            mx(d1, d[k] - d[i]), i = fa[i];
        }
    cout << max(d1, d2);
}

C.P1081 开车旅行

用set预处理然后倍增一下即可

#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
#define f first
#define s second
using namespace std;
typedef long long ll;
typedef long double ld;
using pll = pair<ll, ll>;
const ll N = 1e5 + 5, M = 1e6 + 7;
ll t, n, m, a, b, c, x0;
ll h[N], nx[N][16], nb[N];
ll dis[N][16], da[N][16], db[N];
multiset<pll> s;

inline void cal() {
    s.emplace(5e18, 0), s.emplace(-5e18, 0);
    s.emplace(5e18, 0), s.emplace(-5e18, 0);
    fd(i, n, 1) {
        auto j = s.lower_bound({h[i], 0}), j1 = j--;
        pair<ll, pll> p[4];
        p[0].s = *j, p[1].s = *--j;
        p[2].s = *j1, p[3].s = *++j1;
        fu(j, 0, 3) p[j].f = abs(p[j].s.f - h[i]);
        sort(p, p + 4);
        if (p[0].f < 1e18)
            nb[i] = p[0].s.s, db[i] = p[0].f;
        if (p[1].f < 1e18)
            nx[i][0] = p[1].s.s, dis[i][0] = da[i][0] = da[i][1] = p[1].f;
        s.emplace(h[i], i);
    }
    fu(i, 1, n) nx[i][1] = nb[nx[i][0]], dis[i][1] = dis[i][0] + db[nx[i][0]];
    fu(i, 2, 15) fu(j, 1, n) {
        t = nx[j][i - 1];
        nx[j][i] = nx[t][i - 1];
        dis[j][i] = dis[j][i - 1] + dis[t][i - 1];
        da[j][i] = da[j][i - 1] + da[t][i - 1];
    }
}

inline void ck(ll s, ll d) {
    a = c = 0;
    fd(i, 15, 0) if (nx[s][i] && c + dis[s][i] <= d) {
        c += dis[s][i];
        a += da[s][i];
        s = nx[s][i];
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    fu(i, 1, n) cin >> h[i];
    cal();

    cin >> x0;
    pair<ld, ll> ans = {1, n};
    fu(i, 1, n - 1) {
        ck(i, x0);
        ld x = a ? c * 1.0 / a : 1;
        if (x > ans.f || x == ans.f && h[i] > h[ans.s])
            ans = {x, i};
    }
    cout << ans.s << '\n';

    cin >> m;
    fu(i, 1, m) {
        cin >> a >> b;
        ck(a, b);
        cout << a << ' ' << c - a << '\n';
    }
}

D.P7919 ABC

每次操作最多使相邻相同个数-2。

#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
using namespace std;
const int N = 5e3 + 1;
int n, x, y, t;
char s[N];
int l[N], r[N];

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> s + 1;
    x = 1, y = n;
    while (x < y) {
        while (x < y && s[x] ^ s[x + 1])
            ++x;
        while (y > x && s[y] ^ s[y - 1])
            --y;
        if (x == y)
            break;
        l[++t] = ++x, r[t] = --y;
    }
    if (l[t] > r[t])
        r[t] = n;
    cout << t << '\n';
    fu(i, 1, t) cout << l[i] << ' ' << r[i] << ' ' << "BCA" << '\n';
}

E.CF1607F Robot on the Board 2

一条路径如果如果成环那么整条路径的最大移动次数都是环的长度,
否则move[now] = move[next] + 1,朴素的dfs实现极限要压4e6次栈容易爆空间,
可以优化或者用非递归的写法。

#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; ++a)
#define fd(a, b, c) for (int a = b; a >= c; --a)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
const int N = 2e3 + 5;
short int t, n, m, a, b;
int c, k, dfn;
int dp[N][N], vis[N][N];
char mp[N][N];

void dfs(short int x, short int y) {
    if (x < 1 || x > n || y < 1 || y > m)
        return dp[x][y] = 0, void();
    if (dp[x][y])
        return;
    if (vis[x][y])
        return dp[x][y] = k = dfn - vis[x][y] + 1, void();
    vis[x][y] = ++dfn;
    if (mp[x][y] == 'R') {
        dfs(x, y + 1);
        dp[x][y] = k ? --k, dp[x][y + 1] : dp[x][y + 1] + 1;
    } else if (mp[x][y] == 'L') {
        dfs(x, y - 1);
        dp[x][y] = k ? --k, dp[x][y - 1] : dp[x][y - 1] + 1;
    } else if (mp[x][y] == 'U') {
        dfs(x - 1, y);
        dp[x][y] = k ? --k, dp[x - 1][y] : dp[x - 1][y] + 1;
    } else {
        dfs(x + 1, y);
        dp[x][y] = k ? --k, dp[x + 1][y] : dp[x + 1][y] + 1;
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> t;
    while (t--) {
        cin >> n >> m;
        fu(i, 1, n) {
            cin >> mp[i] + 1;
            memset(dp[i] + 1, 0, m * 8);
            memset(vis[i] + 1, 0, m * 8);
        }
        c = dfn = 0;
        fu(i, 1, n) {
            fu(j, 1, m) {
                if (dfs(i, j), dp[i][j] > c)
                    c = dp[i][j], a = i, b = j;
            }
        }
        cout << a << ' ' << b << ' ' << c << '\n';
    }
}

F.CF1601B Frog Traveler

bfs,已经入过队的点不用重复入队。

#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef long double ld;
const ll N = 3e5 + 10, M = 1e9;
ll t, n, m, k, ans;
ll a, b, c, d, e, f;
ll x[N], y[N], dp[N], lst[N], from[N];
pll p[N];
vector<ll> vx[N], vy[N];
string s;

void out(ll x) {
    if (x ^ n)
        out(lst[x]);
    else
        return;
    cout << x << ' ';
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    fu(i, 1, n) cin >> x[i];
    fu(i, 1, n) cin >> y[i];
    queue<ll> q;
    q.emplace(n);
    ll r = n - 1;
    while (q.size()) {
        a = q.front(), q.pop();
        if (!a) {
            cout << dp[a] << '\n';
            out(0);
            return 0;
        }
        b = a, a += y[a];
        fu(i, a - x[a], r) dp[i] = dp[b] + 1, lst[i] = b, q.emplace(i);
        mn(r, a - x[a] - 1);
    }
    cout << -1;
}

G.P4551 最长异或路径

这题以前的周赛出现过,好像还是加强版,注意到任意两点间异或路径等于两点到根的
异或路径的异或值,建一颗trie就可以贪心出每位的最大值。

#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
using namespace std;
const int N = 2e5;
int n, u, v, ans, no, res;
int to[N], he[N], ne[N], d[N], w[N];
int t[N * 15][2];

void dfs(int u, int fa) {
    for (int i = he[u]; i; i = ne[i]) {
        v = to[i];
        if (v == fa)
            continue;
        d[v] = d[u] ^ w[i];
        dfs(v, u);
    }
}

void insert(int x) {
    u = 0;
    fd(i, 30, 0) {
        v = x >> i & 1;
        if (!t[u][v])
            t[u][v] = ++no;
        u = t[u][v];
    }
}

int q(int x) {
    res = u = 0;
    fd(i, 30, 0) {
        v = x >> i & 1;
        if (t[u][1 - v])
            res += 1 << i, u = t[u][1 - v];
        else
            u = t[u][v];
    }
    return res;
}

int main() {
    scanf("%d", &n);
    fu(i, 1, n - 1) {
        int j = i * 2 - 1;
        scanf("%d%d%d", &u, &v, &w[j]);
        to[j] = v, ne[j] = he[u], he[u] = j;
        to[i * 2] = u, ne[i * 2] = he[v], he[v] = i * 2;
        w[i * 2] = w[j];
    }
    dfs(1, 0);
    fu(i, 1, n) insert(d[i]);
    fu(i, 1, n) ans = max(ans, q(d[i]));
    printf("%d\n", ans);
}

H.P4145 花神游历各国

实现方法很多,关键是注意到每个点修改次数有上限。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e5 + 1;
ll T[N * 4], a[N], fa[N];
ll n, m, q, l, r, t;

ll find(ll x) {
    return fa[x] ? fa[x] = find(fa[x]) : x;
}

void add(ll x, ll y) {
    while (x <= n)
        T[x] += y, x += x & -x;
}

ll qry(ll x) {
    ll r = 0;
    while (x)
        r += T[x], x -= x & -x;
    return r;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n;
    for (ll i = 1; i <= n; ++i)
        cin >> a[i], add(i, a[i]);
    cin >> m;
    while (m--) {
        cin >> q >> l >> r;
        if (l > r)
            swap(l, r);
        if (q ^ 1)
            for (ll i = l; i <= r; i = find(i) ^ i ? fa[i] : i + 1) {
                if (a[i] == 1) {
                    fa[i] = i + 1;
                    continue;
                }
                t = sqrt(a[i]);
                add(i, t - a[i]), a[i] = t;
            }
        else
            cout << qry(r) - qry(l - 1) << '\n';
    }
}
posted @ 2021-11-04 22:03  lemu  阅读(53)  评论(0编辑  收藏  举报
4