Good Bye 2020

传送门

咕了好久的博客...回来诈个尸...

A. Bovine Dilemma

高是固定的,唯一的差别在于底。
由于 \(n\) 很小,所以两两枚举统计差值的不同个数即可。

Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:15:16
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    set<int> s;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (a[i] != a[j]) {
                s.insert(abs(a[i] - a[j]));
            }
        }
    }
    cout << sz(s) << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

B. Last minute enhancements

注意到 \(x_i\) 非递减。
那么从后往前贪心来做就行。

Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:20:36
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    vector<int> cnt(2 * n + 1);
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        --x;
        ++cnt[x];
    }
    int ans = 0;
    for (int i = 2 * n - 1; i >= 0; i--) {
        if (cnt[i]) {
            if (!cnt[i + 1]) {
                ++cnt[i + 1], --cnt[i], ans += 2;
                if (cnt[i] == 0) --ans;
            } else {
                ++ans;
            }
        }
    }
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C. Canine poetry

题意:
给定一个字符串,现在一次操作可以修改任意一个位置的字符。
问最少修改多少次使得该串不存在长度大于1的回文串。

思路:
很有意思的一道题目。
注意到满足一个长度大于1的回文串,必然满足存在长度等于2或者3的回文串。所以我们只需要考虑消除所有长度等于2或者3的回文串。
考虑一个字符最多会影响左右各两个字符,而字符集大小为26,所以一定可以通过枚举找到一个与他们都不相等的字符。
所以从前往后逐个击破即可。

Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:38:39
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    string s;
    cin >> s;
    int n = s.length();
    int ans = 0;
    for (int i = 1; i < n; i++) {
        if (s[i - 1] == s[i] || (i > 1 && s[i] == s[i - 2])) {
            ++ans;
            for (int j = 0; j < 26; j++) {
                if (j != s[i - 1] - 'a' 
                    && (i - 2 < 0 || j != s[i - 2] - 'a')
                    && (i + 1 >= n || j != s[i + 1] - 'a')
                    && (i + 2 >= n || j != s[i + 2] - 'a')
                ) {
                    s[i] = char(j + 'a');
                    break;
                }
            }
        }
    }
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while (T--)
    run();
    return 0;
}

D. 13th Labour of Heracles

题面较绕...不过稍微思考一下就可以发现,贪心来做就行。
代码应该比较好懂。

Code
// Author : heyuhhh
// Created Time : 2020/12/31 09:57:09
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<int> d(n);
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        ++d[u], ++d[v];
    }
    priority_queue<pii> que;
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        que.push(MP(a[i], d[i] - 1));
        ans += a[i];
    }
    cout << ans;
    for (int i = 1; i < n - 1; i++) {
        while (!que.empty()) {
            pii cur = que.top(); que.pop();
            if (cur.se == 0) continue;
            ans += cur.fi;
            --cur.se;
            if (cur.se > 0) que.push(cur);
            break;
        }
        cout << ' ' << ans;
    }
    cout << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

E. Apollo versus Pan

求和式子看似时间复杂度要爆炸,实际上我们可以利用二进制每位独立的性质,提前预处理来做。
大概就是先预处理,然后 \(O(n)\) 消掉后面的两个和式得到 \(\displaystyle f(j)=\sum_{k=1}^n(x_j|x_k)\)
之后又通过预处理,\(O(n)\) 求得答案。

Code
// Author : heyuhhh
// Created Time : 2020/12/31 10:09:58
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 1e9 + 7;
void run() {
    int n;
    cin >> n;
    vector<ll> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<int> has(60);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 60; j++) if (a[i] >> j & 1) {
            ++has[j];
        }
    }
    vector<int> f(n);
    for (int j = 0; j < n; j++) {
        int res = 0;
        for (int bit = 0; bit < 60; bit++) {
            if (a[j] >> bit & 1) {
                res = (res + (1ll << bit) % MOD * n % MOD) % MOD;
            } else {
                res = (res + (1ll << bit) % MOD * has[bit] % MOD) % MOD;
            }
        }
        f[j] = res;
    }
    vector<int> g(60);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 60; j++) {
            if (a[i] >> j & 1) {
                g[j] = (g[j] + f[i]) % MOD;
            }
        }
    }
    int ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 60; j++) if (a[i] >> j & 1) {
            ans = (ans + (1ll << j) % MOD * g[j] % MOD) % MOD;
        }
    }
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

F. Euclid's nightmare

题意:
给出 \(n\)\(m\) 维01向量,每一个向量至多两个 \(1\)
问多少种不同的情况能被这 \(n\) 个向量表出。
要求输出能表示出这些情况的一个最小向量集,这里的最小首先要求集合大小最小,其次向量按照输入顺序的字典序最小。

思路:
显然,我们可以大致将最终的结果分为两类:自由位、与其它位有联系的位。

接下来考虑怎么确定自由位:假设一个向量只有一个自由位,那么可以直接确定;假设一个向量有两个自由位,并且其中之一为自由位,那么这一位也变为自由位。
那我们考虑按顺序进行模拟,但可能出现这种情况:第 \(x\) 位为自由位,现有一个向量第 \(y,z\) 位为1,之后有一个向量第 \(x,y\) 位为1,那么 \(z\) 也“自动”变为了自由位。
所以我们可以考虑用并查集维护连通块来实现上述过程。
注意一个集合中只需要一个原本就是自由位的位即可,这样能满足集合大小最小。

确定与其它位有联系的位的话,按照上述思路自然而然地想到维护连通块。不过计算答案时有差异:假设连通块大小为 \(x\),那么对答案的贡献即为 \(2^{x-1}\)。因为我们只要把某两个缩成一个点,将他们看作一个自由位,最后就会有 \(x-1\) 个自由位。

Code
// Author : heyuhhh
// Created Time : 2020/12/31 10:38:31
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 1e9 + 7;
int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
void run() {
    int n, m;
    cin >> n >> m;
    vector<int> ans;
    int res = 1;
    vector<int> f(m);
    vector<int> siz(m, 1);
    vector<int> has(m);
    iota(all(f), 0);

    auto find = [&] (int x) {
        while (x != f[x]) {
            int t = f[x];
            f[x] = f[f[x]];
            x = t;
        }
        return x;
    };
    auto Union = [&] (int x, int y) {
        x = find(x), y = find(y);
        if (x != y) {
            if (has[x] > has[y]) {
                swap(x, y);
            }
            if (has[x] == 0) {
                f[x] = y;
                siz[y] += siz[x];
                return true;
            }
        }
        return false;
    };
    
    for (int i = 0; i < n; i++) {
        int k;
        cin >> k;
        if (k == 1) {
            int x;
            cin >> x;
            --x;
            int fx = find(x);
            if (!has[fx]) {
                has[fx] = 1;
                ans.emplace_back(i);
            }
        } else {
            int x, y;
            cin >> x >> y;
            --x, --y;
            if (Union(x, y)) {
                ans.emplace_back(i);
            }
        }
    }

    sort(all(ans));
    for (int i = 0; i < m; i++) if (f[i] == i) {
        if (has[i]) res = 1ll * res * qpow(2, siz[i]) % MOD;
        else res = 1ll * res * qpow(2, siz[i] - 1) % MOD;
    }
    cout << res << ' ' << sz(ans) << '\n';
    for (auto it : ans) {
        cout << it + 1 << ' ';
    }
    cout << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

G. Song of the Sirens

题意:
给定 \(s_0,t\) 两个字符串,定义 \(s_i=s_{i-1}t_{i-1}s_{i-1}\)
之后会有 \(q\) 组询问,每组询问给出 \(id\ s\)
现在回答 \(s_{id}\) 里面包含多少 \(s\)
保证所有的 \(s\) 长度之和不超过 \(10^6\)

思路:
这个题和济南的L题简直不能太像,济南就是因为两个bug没有出L呜呜呜(错失金牌)
一开始还想利用上 \(s_0\leq 100\) 这个条件,貌似最后根本用不上...
对于每一组询问,我们只需要找到最小的 \(k\),使得 \(|s_k|\geq |s|\)。这一段我们暴力计算答案。
之后因为是直接拼接,那么我们只需要利用 \(s_k\) 的头和尾。提前预处理所有的三元组 \((x,y,i)\),分别表示尾巴 \(x\),头部 \(y\),满足 \(x+y+1=|s|\) 并且对应的与 \(s\) 相等,并且 \(s[x] - 'a'= i\)
我们记 \(f(i)\) 表示满足上述条件的三元组个数,\(0\leq i<26\)。这里可以hash或者kmp之类的计算。

接下来考虑计算答案。因为询问的 \(id\) 可能很大,虽然每一个位置的贡献我们很容易求,为2的若干次幂,但不可能一位一位去求。
思考后其实可以发现,我们只需要对每一类字符单独统计贡献即可,结合“每一类字符两两相邻的位置差不变”这一性质。所以对每一类字符最后乘以或者除以一个2的若干次幂就快速求得答案啦。
细节见代码吧,感觉类似预处理的思想很常见,一般的hash其实就类似于这样。

Code
// Author : heyuhhh
// Created Time : 2021/01/05 11:39:55
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e6 + 5, MOD = 1e9 + 7, INV = (MOD + 1) / 2;

inline int add(int x, int y) {
    return x + y >= MOD ? x + y - MOD : x + y;
}
inline int dec(int x, int y) {
    return x - y < 0 ? x - y + MOD : x - y;
}
inline int mul(int x, int y) {
    return 1ll * x * y % MOD;
}

int pow2[N], inv2[N];
void init() {
    pow2[0] = inv2[0] = 1;
    for (int i = 1; i < N; i++) {
        pow2[i] = mul(pow2[i - 1], 2);
        inv2[i] = mul(inv2[i - 1], INV);
    }
}

typedef unsigned long long ull;
template <unsigned mod, unsigned base>
struct rolling_hash {
    unsigned int pg[N], val[N]; // val:1,2...n
    rolling_hash() {
        pg[0] = 1;
        for(int i = 1; i < N; i++) pg[i] = 1ull * pg[i - 1] * base % mod;
        val[0] = 0;
    }
    void build(const char *str) {
        for(int i = 0; str[i]; i++) {
            val[i + 1] = (str[i] + 1ull * val[i] * base) % mod;
        }
    }
    unsigned int operator() (int l, int r) {
        ++r; // 
        return (val[r] - 1ull * val[l] * pg[r - l] % mod + mod) % mod;
    }
};
struct dm_hasher {
    //str:0,1...len-1
    rolling_hash<997137961, 753> h1;
    rolling_hash<1003911991, 467> h2;
    void build(const char *str) {
        h1.build(str); h2.build(str);
    }
    ull operator() (int l, int r) {
        return ull(h1(l, r)) << 32 | h2(l, r);
    }
}hasher, hasher2;

void run() {
    int n, q;
    cin >> n >> q;
    string s0, t;
    cin >> s0 >> t;
    vector<vector<int>> pos(26);
    for (int i = 0; i < n; i++) {
        pos[t[i] - 'a'].emplace_back(i);
    }

    vector<int> c(n);
    vector<int> last(26);
    for (int i = 0; i < 26; i++) {
        for (int j = sz(pos[i]) - 1; j >= 0; j--) {
            if (j == sz(pos[i]) - 1) {
                c[pos[i][j]] = 1;
                last[i] = pos[i][j];
            } else {
                c[pos[i][j]] = add(c[pos[i][j + 1]], pow2[last[i] - pos[i][j]]);
            }
        }
    }
    while (q--) {
        int id;
        string s;
        cin >> id >> s;
        int len = s.length();
        int k;
        string res = s0;
        int ans = 0;
        for (int i = 0; i <= id; i++) {
            if (res.length() >= len) {
                k = i;
                break;
            }
            if (i < id) res += t[i] + res;
        }
        if (res.length() < len) {
            cout << 0 << '\n';
            continue;
        }
        hasher.build(res.c_str());
        hasher2.build(s.c_str());
        ull val = hasher2(0, len - 1);
        int resLen = res.length();
        for (int i = 0; i + len - 1 < resLen; i++) {
            int j = i + len - 1;
            if (hasher(i, j) == val) {
                ++ans;
            }
        }
        ans = mul(ans, pow2[id - k]);
        vector<int> f(26);
        for (int j = 1; j < len; j++) {
            if (hasher(resLen - j, resLen - 1) == hasher2(0, j - 1)
                && hasher(0, len - j - 2) == hasher2(j + 1, len - 1)
                ) {
                    ++f[s[j] - 'a'];
                }
        }
        if (hasher(0, len - 2) == hasher2(1, len - 1)) {
            ++f[s[0] - 'a'];
        }
        // add t[k]....t[id - 1]
        for (int i = 0; i < 26; i++) if (f[i]) {
            int lAt = lower_bound(all(pos[i]), k) - pos[i].begin();
            int rAt = lower_bound(all(pos[i]), id) - pos[i].begin() - 1;
            int res = 0;
            if (lAt == sz(pos[i])) continue;
            if (rAt == sz(pos[i]) - 1) {
                res = mul(f[i], mul(c[pos[i][lAt]], pow2[id - 1 - last[i]]));
            } else {
                int tmp = dec(c[pos[i][lAt]], c[pos[i][rAt + 1]]);
                res = mul(f[i], mul(tmp, inv2[last[i] - id + 1]));
            }
            ans = add(ans, res);
        }
        cout << ans << '\n';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    run();
    return 0;
}
posted @ 2021-01-05 17:02  heyuhhh  阅读(266)  评论(0编辑  收藏  举报