HDU多校 2024R1

T1

\(A\) 的所有循环位移哈希一下扔 set 里,拿 \(B\) 的所有长为 \(|A|\) 的子串查一遍即可。

代码
#include <iostream>
#include <set>
using namespace std;
set<unsigned long long> st;
const int B = 2333;
unsigned long long pw[2000005];
int main() {
    int tc;
    cin >> tc;
    pw[0] = 1;
    for (int i = 1; i <= 2000000; i++) pw[i] = pw[i - 1] * B;
    while (tc--) {
        st.clear();
        string a, b;
        cin >> a >> b;
        int n = a.size();
        a = a + a;
        int m = b.size();
        a = ' ' + a, b = ' ' + b;
        int tmp = 0;
        for (int i = 1; i < n; i++) tmp = tmp * B + a[i] - 'A' + 1;
        for (int i = 1; i <= n; i++) {
            int t = i + n - 1;
            tmp = tmp * B + a[t] - 'A' + 1;
            st.insert(tmp);
            tmp -= pw[n - 1] * (a[i] - 'A' + 1);
        }
        tmp = 0;
        int ans = 0;
        for (int i = 1; i < n; i++) tmp = tmp * B + b[i] - 'A' + 1;
        for (int i = 1; i + n - 1 <= m; i++) {
            int t = i + n - 1;
            tmp = tmp * B + b[t] - 'A' + 1;
            ans += st.count(tmp);
            tmp -= pw[n - 1] * (b[i] - 'A' + 1);
        }
        cout << ans << "\n";
    }
    return 0;
}

T2

背包即可。

代码
#include <iostream>
#define int long long
using namespace std;
const int inf = 0x7ffffffffffffff;
int dp[1005][4005];
int a[1005];
int b[1005];
int c[1005];
int d[1005];
signed main() {
    int tc;
    cin >> tc;
    while (tc--) {
        int n, k;
        cin >> n >> k;
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= k; j++) 
                dp[i][j] = inf;
        }
        dp[0][0] = 0;
        for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i] >> d[i];
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= 4000; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j) 
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + a[i]);
                if (j > 1) 
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - 2] + b[i]);
                if (j > 2) 
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - 3] + c[i]);
                if (j > 3) 
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - 4] + d[i]);
            }
        }
        cout << dp[n][k] << "\n";
    }
    return 0;
}

T3

又有 \(\max\) 又有绝对值,看起来很不爽。分讨一下拆式子,发现只需要对每个值 \(x\) 记录小于 \(x\) 的个数,小于 \(x\) 的所有数的和,大于 \(x\) 的所有数的和以及大于 \(x\) 的所有数的平方和即可完成 \(\mathcal{O}(1)\) 时间内加入 / 删除一个数并维护答案。使用 dsu on tree 加上树状数组即可做到 \(\mathcal{O}(n \log^2n)\)

代码
#include <iostream>
#define int long long
#define lowbit(x) ((x) & (-(x)))
using namespace std;
#define nnc getchar
inline int read() {
    int ret = 0;
    char c = nnc();
    while (c < '0' || c > '9') c = nnc();
    while ('0' <= c && c <= '9') ret = ret * 10 + c - 48, c = nnc();
    return ret;
}
int n;
unsigned long long a[500005];
int head[500005], nxt[1000005], to[1000005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int sz[500005], son[500005];
int dfn[500005], _dfn[500005], ncnt;
void dfs1(int x, int fa) {
    sz[x] = 1;
    _dfn[dfn[x] = ++ncnt] = x;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            dfs1(v, x);
            sz[x] += sz[v];
            if (sz[v] > sz[son[x]]) 
                son[x] = v;
        }
    }
}
struct BIT {
    pair<unsigned long long, unsigned long long> pbit[1000005], sbit[1000005];
    pair<unsigned long long, unsigned long long> pquery(int x) {
        pair<unsigned long long, unsigned long long> ret(0, 0);
        for (; x; x -= lowbit(x)) ret.first += pbit[x].first, ret.second += pbit[x].second;
        return ret;
    }
    pair<unsigned long long, unsigned long long> squery(int x) {
        pair<unsigned long long, unsigned long long> ret(0, 0);
        for (; x <= 1000000; x += lowbit(x)) ret.first += sbit[x].first, ret.second += sbit[x].second;
        return ret;
    }
    void add(int x, unsigned long long y) {
        unsigned long long tx = x;
        for (; x <= 1000000; x += lowbit(x)) pbit[x].first += y, pbit[x].second += tx * y;
        for (x = tx; x; x -= lowbit(x)) sbit[x].first += y * tx, sbit[x].second += tx * tx * y;
    }
} bit;
unsigned long long cur, Ans;
void addd(unsigned long long x, int v = 1) {
    x = a[x];
    pair<int, int> qp, qs;
    qp = bit.pquery(x - 1), qs = bit.squery(x + 1);
    cur += v * (x * (x * qp.first - qp.second) + qs.second - x * qs.first);
    bit.add(x, (unsigned long long)v);
}
void Add(int x) {
    for (int i = dfn[x]; i < dfn[x] + sz[x]; i++) addd(_dfn[i]);
}
void Del(int x) {
    for (int i = dfn[x]; i < dfn[x] + sz[x]; i++) addd(_dfn[i], -1);
}
void dfs2(int x, int fa) {
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa && v != son[x]) 
            dfs2(v, x), Del(v);
    }
    if (son[x]) 
        dfs2(son[x], x);
    addd(x);
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa && v != son[x]) 
            Add(v);
    }
    Ans ^= cur * 2;
}
signed main() {
    n = read();
    for (int i = 1; i < n; i++) {
        int u, v;
        u = read(), v = read();
        add(u, v);
        add(v, u);
    }
    for (int i = 1; i <= n; i++) a[i] = read();
    dfs1(1, 0);
    dfs2(1, 0);
    cout << Ans << "\n";
    return 0;
}

T4

线段树分治一下,发现需要在可撤销并查集中支持给某集合中每个数加上一个给定数。考虑使用懒标记,当一个点 \(x\) 被从 \(y\) 断开时,将 \(y\) 的懒标记传到 \(x\) 上。但是这样会使新加入的数的答案多算,于是当 \(x\) 被接到 \(y\) 上时,把 \(y\) 的懒标记从 \(x\) 中减去。这样就没有问题了。

T6

转化为从三个相同的序列中分别选出一个子序列使得全部相同的方案数。\(f[i][j][k]\) 表示分别匹配到 \(i, j, k\),三维前缀和优化即可。

代码
#include <iostream>
#define int long long
using namespace std;
const int P = 998244353;
int n;
int dp[255][255][255];
int a[255];
signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                dp[i][j][k] = dp[i - 1][j][k] + dp[i][j - 1][k] + dp[i][j][k - 1] - dp[i - 1][j - 1][k] - dp[i - 1][j][k - 1] - dp[i][j - 1][k - 1] + dp[i - 1][j - 1][k - 1];
                if (a[i] == a[j] && a[j] == a[k]) 
                    dp[i][j][k] += dp[i - 1][j - 1][k - 1] + 1;
                dp[i][j][k] = (dp[i][j][k] % P + P) % P;
            }
        }
    }
    cout << dp[n][n][n] << "\n";
    return 0;
}

T7

正难则反,考虑若三个点不成环,当且仅当三个点中存在一个点有两个入度。先把所有三元组计入答案,再减去不成环的三元组即可。每个不成环的三元组在其中有两个入度的点计算其贡献,统计入度三维偏序一下即可。

代码
#include <iostream>
#include <algorithm>
#define lowbit(x) ((x) & (-(x)))
#define int long long
using namespace std;
int n;
struct node {
    int id, x, y;
} a[200005], ta[200005], tb[200005];
int ans1[200005];
int ans2[200005];
int* ans;
struct BIT {
    int bit[200005];
    void add(int x, int y) { for (; x <= 200000; x += lowbit(x)) bit[x] += y; }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret += bit[x];
        return ret;
    }
} bit;
void Solve(int l, int r) {
    if (l == r) 
        return;
    int mid = (l + r) >> 1;
    Solve(l, mid), Solve(mid + 1, r);
    int j = l;
    for (int i = mid + 1; i <= r; i++) {
        while (j <= mid && a[j].x < a[i].x) bit.add(a[j].y, 1), ++j;
        ans[a[i].id] += bit.query(a[i].y - 1);
    }
    for (int i = l; i < j; i++) bit.add(a[i].y, -1);
    int a1 = mid - l + 1;
    int b1 = r - mid;
    for (int i = 1; i <= a1; i++) ta[i] = a[i + l - 1];
    for (int i = 1; i <= b1; i++) tb[i] = a[mid + i];
    int i = 1, k = l;
    j = 1;
    for (; i <= b1; i++) {
        while (j <= a1 && ta[j].x < tb[i].x) a[k] = ta[j++], k++;
        a[k++] = tb[i];
    }
    while (j <= a1) a[k] = ta[j++], k++;
}
int cnt[200005];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
    cin >> n;
    if (n <= 2) {
        cout << 0 << "\n";
        return 0;
    }
    for (int i = 1; i <= n; i++) cin >> a[i].x;
    for (int i = 1; i <= n; i++) cin >> a[i].y, a[i].id = i;
    ans = ans1;
    Solve(1, n);
    sort(a + 1, a + n + 1, [](node a, node b) { return a.id > b.id; });
    for (int i = 1; i <= n; i++) a[i].x = n - a[i].x + 1, a[i].y = n - a[i].y + 1;
    ans = ans2;
    Solve(1, n);
    int Ans = n * (n - 1) * (n - 2) / 6;
    for (int i = 1; i <= n; i++) {
        int l = ans1[i], r = n - i - ans2[i];
        Ans -= (l * (l - 1) / 2 + r * (r - 1) / 2 + l * r);
    }
    cout << Ans << "\n";
    return 0;
}

T8

位运算按位独立,对每一位分别考虑,对于 \(n\) 的每一位,若为 \(0\) 则有 \(4\) 种方案,否则有 \(12\) 种。每一位的方案数乘起来即可。

代码
#include <iostream>
using namespace std;
int main() {
    int tc;
    cin >> tc;
    while (tc--) {
        int n, k;
        cin >> n >> k;
        long long ans = 1;
        while (k--) {
            if (n & 1) 
                ans *= 12;
            else 
                ans *= 4;
            n >>= 1;
        }
        cout << ans << "\n";
    }
    return 0;
}

T12

离散化,对每个区域计算其贡献。很显然被覆盖次数相同的区域对所有答案的贡献系数都相同,因此直接合并。然后 \(n^2\) 枚举覆盖次数和 \(k\) 算贡献即可。

代码
#include <iostream>
#include <algorithm>
#include <map>
#define int long long
using namespace std;
const int P = 998244353;
int n;
int x1[2005], yy1[2005], x2[2005], y2[2005];
int d[4005], dcnt;
int nx, ny;
int _mpx[4005], _mpy[4005];
int cov[4005][4005];
int fac[4005], inv[4005], ifac[4005];
void Cpre(int x) {
    fac[0] = fac[1] = inv[0] = inv[1] = ifac[0] = ifac[1] = 1;
    for (int i=  2; i <= x; i++) {
        fac[i] = fac[i - 1] * i % P;
        inv[i] = (P - P / i) * inv[P % i] % P;
        ifac[i] = ifac[i - 1] * inv[i] % P;
    }
}
int s[2005];
map<int, int> mp;
signed main() {
    Cpre(4000);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> x1[i] >> yy1[i] >> x2[i] >> y2[i];
    for (int i = 1; i <= n; i++) d[++dcnt] = x1[i], d[++dcnt] = x2[i];
    sort(d + 1, d + dcnt + 1);
    for (int i = 1; i <= dcnt; i++) (d[i] != d[i - 1]) ? (_mpx[mp[d[i]] = mp[d[i - 1]] + 1] = d[i]) : 0;
    for (int i = 1; i <= n; i++) x1[i] = mp[x1[i]], x2[i] = mp[x2[i]];
    nx = mp[d[dcnt]];
    dcnt = 0, mp.clear();
    for (int i = 1; i <= n; i++) d[++dcnt] = yy1[i], d[++dcnt] = y2[i];
    sort(d + 1, d + dcnt + 1);
    for (int i = 1; i <= dcnt; i++) (d[i] != d[i - 1]) ? (_mpy[mp[d[i]] = mp[d[i - 1]] + 1] = d[i]) : 0;
    for (int i = 1; i <= n; i++) yy1[i] = mp[yy1[i]], y2[i] = mp[y2[i]];
    ny = mp[d[dcnt]];
    for (int i = 1; i <= n; i++) {
        cov[x1[i]][yy1[i]]++;
        cov[x2[i]][yy1[i]]--;
        cov[x1[i]][y2[i]]--;
        cov[x2[i]][y2[i]]++;
    }
    for (int i = 1; i <= nx; i++) {
        for (int j = 1; j <= ny; j++) 
            cov[i][j] += cov[i - 1][j] + cov[i][j - 1] - cov[i - 1][j - 1];
    }
    for (int i = 1; i < nx; i++) {
        for (int j = 1; j < ny; j++) {
            int S = (_mpx[i + 1] - _mpx[i]) * (_mpy[j + 1] - _mpy[j]);
            s[cov[i][j]] += S;
        }
    }
    for (int k = 1; k <= n; k++) {
        int ans = 0;
        for (int x = 1; x <= n - k; x++) 
            ans += s[x] % P * (P + 1 - fac[n - x] * fac[n - k] % P * ifac[n] % P * ifac[n - x - k] % P) % P;
        for (int x = n - k + 1; x <= n; x++) ans = (ans + s[x]) % P;
        cout << ans % P << "\n";
    }
    return 0;
}
posted @ 2024-07-21 22:16  forgotmyhandle  阅读(39)  评论(0编辑  收藏  举报