2021 ICPC新疆省赛

2021 ICPC新疆省赛

虽然是第一个10题, 但输在了罚时, zto ljs orz

A - Chino With String

一眼是ac自动机, 然后\(n \leqslant 1e9\) 就是矩阵加速了

但这里并不是乘法运算

路径只能算一个, 所以矩阵外层是取max, 内层是路径权值相加

记得初始化\(ans = -inf\) 这里白wa2发

struct AC {
    int tr[N][26], fail[N], tot;
    ll ed[N];
    void insert(char *s, int k) {
        int p = 0;
        for (int i = 0; s[i]; p = tr[p][s[i++] - 'a'])
            if (!tr[p][s[i] - 'a']) tr[p][s[i] - 'a'] = ++tot;
        ed[p] += k;
    }
    void build() {
        queue<int> q;
        rep (i, 0, 25) if (tr[0][i]) q.push(tr[0][i]);
        while (!q.empty()) {
            int p = q.front(); q.pop();
            rep (i, 0, 25)
                if (tr[p][i]) {
                    fail[tr[p][i]] = tr[fail[p]][i];
                    ed[tr[p][i]] += ed[tr[fail[p]][i]]; q.push(tr[p][i]);
                }
                else tr[p][i] = tr[fail[p]][i];
        }
    }
} ac;

struct Matrix {
    vector<vector<ll>> a;
    Matrix(int N = 0, int M = 0) {
        vector<vector<ll>>(N, vector<ll>(M, -1e18)).swap(a);
    }
    Matrix operator*(const Matrix& b) {
        Matrix c(a.size(), b.a[0].size());
        rep (i, 0, a.size() - 1) rep (j, 0, b.a[0].size() - 1)
            rep (k, 0, b.a.size() - 1)
                c.a[i][j] = max(c.a[i][j], a[i][k] + b.a[k][j]);
        return c;
    }
};

Matrix qpow(Matrix a, ll b) {
    Matrix c = a; --b;
    for (; b; b >>= 1, a = a * a)
        if (b & 1) c = c * a;
    return c;
}

int n, m, _, k;
char s[205];
ll ans = -2e18;

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    rep (i, 1, m) cin >> s >> k, ac.insert(s, k);
    ac.build(); Matrix a(ac.tot + 1, ac.tot + 1);
    rep (i, 0, ac.tot) rep (k, 0, 25)
        a.a[i][ac.tr[i][k]] = ac.ed[ac.tr[i][k]];
    a = qpow(a, n);
    for (auto &x : a.a[0]) ans = max(ans, x);
    cout << ans;
    return 0;
}

B - Cocktail With Hearthstone

组合数学, 只不过,

  1. a = n, b = m 没这种情况 0
  2. a = n, b != 0, 此时 \(C(a, b)\) 只能从 \(C(a - 1, b)\) 转移过来, 所以是 \(\frac{C(a - 1, b)}{2}\)
  3. a != 0, b = m, 此时 \(C(a, b)\) 只能从 \(C(a, b- 1 )\) 转移过来, 所以是 \(\frac{C(a, b - 1)}{2}\)
  4. 其他的直接组合数学 \(C(a, b)\)

输出答案的时候别忘了乘比赛的人数也就是\(2^{n + m - a - b}\)

ll facinv[N], inv[N], fac[N], qp[N];

ll C(int n, int m) { return fac[n] * facinv[m] % mod * facinv[n - m] % mod; }

void init(int n) {
    fac[0] = fac[1] = facinv[0] = facinv[1] = inv[0] = inv[1] = qp[0] = 1;
    qp[1] = 2;
    for (int i = 2; i <= n; ++i)
        fac[i] = fac[i - 1] * i % mod,
        inv[i] = inv[mod % i] * (mod - mod / i) % mod,
        facinv[i] = facinv[i - 1] * inv[i] % mod,
        qp[i] = qp[i - 1] * 2 % mod;
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m >> _; init(n + m);
    while (_--) {
        int a, b; cin >> a >> b;
        if (a == n && b == m) cout << "0\n";
        else if (a == n && b)
            cout << C(n + b - 1, n - 1) * qp[m - b] % mod << '\n';
        else if (b == m && a)
            cout << C(a + m - 1, a) * qp[n - a] % mod << '\n';
        else cout << C(a + b, a) * qp[n + m - a - b] % mod << '\n';
    }
    return 0;
}

C - Chino With Minimum

罚坐1h, 但就是不会做, 没办法, 看出来是斜率dp了, 可就是不会, 直接罚坐

人急了什么都干得出来, 除了斜率dp还有插头dp

D - Cocktail With Swap

直接暴力并查集, 把能交换的集合合并, 维护个集合的字母数量

int f[N], cnt[N][26];
char s[N];

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> l >> r >> s + 1;
    for (int i = 1; i <= n; ++i) f[i] = i, ++cnt[i][s[i] - 'a'];
    if (l == r) {
        for (int i = 1; i <= l; ++i) for (int j = i + l; j <= n; j += l)
            ++cnt[i][s[j] - 'a'], f[j] = i;
    } else {
        for (int i = 2; i <= n - l; ++i)
            ++cnt[1][s[i] - 'a'], f[i] = 1;
        for (int i = max(n - l + 1, 1 + l); i <= n; ++i)
            ++cnt[1][s[i] - 'a'], f[i] = 1;
    }
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j < 26; ++j) if (cnt[f[i]][j]) {
            cout << char('a' + j);
            --cnt[f[i]][j]; break;
        }
    return 0;
}

E - Is The Order A Rabbit ??

预处理了每天最小值的前缀和, 一直wa, 干脆直接卖的时候\(O(n)\)求, 本身复杂度没变

贪心即可, 维护当前的最大出售价格即可

int a[N][2];
ll ans;

int work(int &i, int day) {
    if ((i & 1) && i + 2 <= day) {
        int c = a[day >> 1][day & 1] - min(a[i + 1 >> 1][0], a[i + 1 >> 1][1]);
        return i += 2, max(0, c);
    }
    if (i & 1) return ++i, 0;
    int c = a[day >> 1][day & 1] - a[i >> 1][1];
    return ++i, max(c, 0);
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n; priority_queue<pair<int, int>> q;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i][0] >> a[i][1];
        q.push({a[i][0], i << 1}); q.push({a[i][1], i << 1 | 1});
    }
    for (int i = 1; !q.empty();) {
        int day = q.top().second; q.pop();
        while (i < day) ans += work(i, day);
    }
    cout << ans;
    return 0;
}

F - Chino With Ball

跟那个蚂蚁走独木桥没啥区别, 就是让你按序输出, 排个序就好了

int a[N], b[N];
pair<int, int> rk[N];

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) {
        int d; cin >> rk[i].first >> d;
        rk[i].second = i;
        b[i] = rk[i].first + d * k;
    }
    sort(b + 1, b + 1 + n);
    sort(rk + 1, rk + 1 + n);
    for (int i = 1; i <= n; ++i)
        a[rk[i].second] = b[i];
    for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
    return 0;
}

G - Cocktail With Snake

签到

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    for (cin >> _; _; --_) {
        ll n, m, k; cin >> n >> m >> k;
        ll a = k / n; k %= n;
        if (a & 1) {
            cout << a + n - k - 1 << '\n';
        } else {
            cout << a + k << '\n';
        }
    }
    return 0;
}

H - Cocktail With Pony

写这道题的时候是过了就第一了, 没管住手, 改一下交一下,

就是输在了这道题的罚时, 看ljs卡了半天a, 以为他做不出了, 就想题数10压制了, 结果反而输在了罚时

本身是到模拟, 注意到, 🐏不能跨越🐺, 就随便模拟就能过

int n, m, _, k;
int v1, v2, x1, x2;

void print(int x) { cout << x << '\n'; }

bool dfs(int x1, int x2, int dep) {
    if (dep & 1) {
        if (x2 + v2 <= n) return dfs(x1, x2 + v2, dep + 1);
        if (x1 == n - 1)  return print(dep), 1;
        int c = x2 + v2 - n;
        x2 = c & 1 ? n - 1 : n;
        return dfs(x1, x2, dep + 1);
    }
    if (x2 - x1 <= v1) return print(dep), 1;
    return dfs(x1 + v1, x2, dep + 1);
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    for (cin >> _; _; --_) {
        cin >> n >> v1 >> v2 >> x1 >> x2;
        if (x1 > x2) x1 = n - x1 + 1, x2 = n - x2 + 1;
        dfs(x1, x2, 1);
    }
    return 0;
}

I - Chino With Mates

正解尺取, 但这复杂度, 写二分更快, 注意上下届, 即可

ll ans;

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m; vector<int> a, b;
    for (int i = 1; i <= n; ++i) cin >> k, a.emplace_back(k);
    for (int i = 1; i <= m; ++i) cin >> k, b.emplace_back(k);
    cin >> l >> r;
    sort(a.begin(), a.end()); sort(b.begin(), b.end());
    for (auto &x : a)
        if (x < 0)
            ans += upper_bound(b.begin(), b.end(), floor((double)l / x)) - lower_bound(b.begin(), b.end(), ceil(double(r) / x));
        else if (x > 0)
            ans += upper_bound(b.begin(), b.end(), floor((double)r / x)) - lower_bound(b.begin(), b.end(), ceil(double(l) / x));
        else if (l <= 0 && r >= 0) ans += m;
    cout << ans;
    return 0;
}

J - Cocktail With Not 2b

赛前hhg提了一嘴状压, 我一看, \(log2(1e6) < 20\), 直接写状压去了

然后wa了, 就没在写这个思路了, 赛后发现是变量写错了...

这是状压

const int N = 1e6 + 5;

int n, m, _, k, l, r;
int cnt[N], ans;
vector<int> st[21];

void init(int x) {
    for (int k = 0; k < 1 << x; ++k) {
        bool f = 1;
        for (int i = 1; i < x; ++i) if ((k >> i & 1) && (k >> i - 1 & 1)) {
            f = 0; break;
        }
        if (f) st[x].emplace_back(k);
    }
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> m, ++cnt[m];
    for (int i = 1; i <= 1e6; ++i) if (cnt[i] && ((i & 1) || !cnt[i >> 1])) {
        int len = 0;
        for (int j = i; j <= 1e6 && cnt[j]; j <<= 1) ++len;
        if (st[len].empty()) init(len);
        int res = n;
        for (auto &x : st[len]) {
            int cur = 0;
            for (int j = 0; j < len; ++j) if (!(x >> j & 1))
                cur += cnt[i << j];
            res = min(res, cur);
        }
        ans += res;
    }
    cout << ans;
    return 0;
}

然后正解是直接dp完事, 按dp来想确实是到签到题

int n, m, _, k, l, r;
int cnt[N], ans, f[N][2];
 
int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> m, ++cnt[m];
    for (int i = 1; i <= 1e6; ++i) {
        if (i & 1) {
            f[i][0] = cnt[i];
            f[i][1] = 0;
        } else {
            f[i][0] = min(f[i >> 1][0], f[i >> 1][1]) + cnt[i];
            f[i][1] = f[i >> 1][0];
        }
    }
    for (int i = 1e6; i * 2 > 1e6; --i) ans += min(f[i][0], f[i][1]);
    cout << ans;
    return 0;
}

K - Chino With C language

签到, 而且并不是题目说说的\(memcpy\)并不会覆盖

\(memcpy\)会检测从左或从右赋值, 总有一个方向不会覆盖, 这个\(copy\)并不会发生覆盖

当然按题意写就完事了, 签到

char s[N], t[N];

int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> s + 1 >> l >> r >> k;
    memcpy(t + 1, s + 1, n * sizeof(char));
    memmove(t + r, t + l, k * sizeof(char));
    for (int i = 0; i < k; ++i) s[r + i] = s[l + i];
    cout << s + 1 << '\n' << t + 1;
    return 0;
}
posted @ 2021-05-23 19:02  洛绫璃  阅读(1390)  评论(2编辑  收藏  举报