Loading

UNR#4 Day2

A. 同构判定鸭

既然要输出字典序最小的坏串,直觉是它肯定不长(证明不会,看 官解),考虑确定坏串的长度。

\(S_{u, k}\) 表示从 \(u\) 出发长度为 \(k\) 的路径的配对串的集合,我们要做的就是找到最小的 \(k\),使得 \(\bigcup {S_1}_{u, k} \ne \bigcup {S_2}_{u, k}\)

Hash 即可。

确定长度之后贪心确定坏串,需要记录长度为 \(k\) 的路径中以某个字母开头的哈希值,可以 DP 实现。

时间复杂度 \(\mathcal O(nm)\)

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

constexpr int N = 510, M = 3010, MOD = 1e9 + 7;

mt19937 eng(random_device{}());

int w[N << 1][128];

struct Graph {
    int n, m;
    int hs[N << 1], val[N], con[N], f[N << 1][N];

    struct Edge {int u, v; char c;} e[M];

    inline void init() {fill(val + 1, val + n + 1, 1);}
    inline void lock() {memcpy(val, con, sizeof(val));}

    void input() {
        cin >> n >> m;
        for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].c;
        init();
    }
    
    int calc(int x) {
        memset(con, 0, sizeof(con));
        for (int i = 1; i <= m; i++) con[e[i].v] = (con[e[i].v] + 1ll * val[e[i].u] * w[x][e[i].c]) % MOD;
        int res = 0;
        for (int i = 1; i <= n; i++) res = (res + con[i]) % MOD;
        return lock(), res;
    }

    void dp(int T) {
        for (int i = 1; i <= n; i++) f[T + 1][i] = 1;
        for (int i = T; i; i--) for (int j = 1; j <= m; j++) {
            f[i][e[j].u] = (f[i][e[j].u] + 1ll * w[i][e[j].c] * f[i + 1][e[j].v]) % MOD;
        }
    }

    int spec(int x, char c) {
        memset(con, 0, sizeof(con));
        for (int i = 1; i <= m; i++) if (e[i].c == c) con[e[i].v] = (con[e[i].v] + 1ll * val[e[i].u] * w[x][e[i].c]) % MOD;
        int res = 0;
        for (int i = 1; i <= n; i++) res = (res + 1ll * con[i] * f[x + 1][i]) % MOD;
        return res;
    }
} G1, G2;

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    G1.input(), G2.input();
    int n = 1e5;
    for (int i = 1; i <= n; i++) for (int j = 'a'; j <= 'z'; j++) w[i][j] = eng() % MOD;
    int len = 0;
    for (int i = 1; i <= n; i++) if (G1.calc(i) != G2.calc(i)) {len = i; break;}
    if (!len) {cout << "Same"; return 0;}
    G1.dp(len), G2.dp(len);
    G1.init(), G2.init();
    for (int i = 1; i <= len; i++) for (char c = 'a'; c <= 'z'; c++) {
        if (G1.spec(i, c) != G2.spec(i, c)) {
            cout << c;
            G1.lock(), G2.lock();
            break;
        }
    }
    return 0;
}

B. 己酸集合

对每个 \(j\) 求满足 \(x_i^2 + (y_i - z_j)^2 \le R_j^2\)\(i\) 的个数。

展开,写成 \(x_i^2 + y_i^2 \le 2z_jy_i + R_j^2 - z_j^2\)

\(X_i = y_i, Y_i = x_i^2 + y_i^2, k_j = 2z_j, b_j = R_j^2 - z_j^2\),则原式又可以写作:\(k_jX_i + b_j \ge Y_i\),即 \((X_i, Y_i)\) 在直线 \(l_j: y = k_jx + b_j\) 的下方。

对每个询问暴力判断,时间复杂度 \(\mathcal O(qn)\)

把询问按 \(k_j\) 离线,维护点的相对顺序,然后二分。

具体地,在一个确定的 \((k, b)\) 下,\((X_i, Y_i)\) 优于 \((X_j, Y_j)\) 当且仅当 \(Y_i - kX_i < Y_j - kX_j\),又因为最开始 \(k \to -\infin\),所以按 \(X\) 升序(\(X\) 相同时按 \(Y\) 升序)排序是对的,然后需要每个满足 \(i < j\) 的点对 \((i, j)\) 求出什么时候交换位置即可。

  • 如果 \(X_i = X_j\),那不可能有位置的交换。

  • 否则则由最初的排序有 \(X_j > X_i\),要满足的新式子是 \(Y_j - kX_j < Y_i - kX_i\),即 \(k(X_i - X_j) < Y_i - Y_j\),同除 \(X_i - X_j\) 不等号方向要改变,即 \(k > \dfrac{Y_i - Y_j}{X_i - X_j}\),然后我们就知道何时 \((i, j)\) 应交换位置了。

最后对每个询问在维护相对顺序的同时二分查找就能数出个数了,时间复杂度 \(\mathcal O((n^2 + q)\log n)\)

分块平衡一下预处理和查询的时间复杂度,设块长为 \(B\),则时间复杂度为 \(\mathcal O\left(\left(nB + \dfrac{qn}B\right)\log n\right)\)

\(B = \sqrt q\) 最优,时间复杂度 \(\mathcal O\left(n\sqrt q\log n\right)\)

实际实现时,因为 \(\mathcal O(nB\log n)\) 的预处理部分的常数比较大,所以可以给 \(B\) 带一个 \(\left(\dfrac12, 1\right)\) 中的系数再次平衡,我带的是 \(\dfrac23\)

代码

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

constexpr int N = 12010, Q = 1e6 + 10;

int n, qn, ans[Q], pos[N];

struct Point {
    ll x, y;

    Point() {}
    Point(ll x, ll y) : x(y), y(x * x + y * y) {}

    bool operator<(const Point &rhs) const {return x != rhs.x ? x < rhs.x : y < rhs.y;}
} p[N];

struct Query {
    int k; ll b; int id;

    Query() {}
    Query(ll z, ll r, int id) : k(z << 1), b(r * r - z * z), id(id) {}

    bool operator<(const Query &rhs) const {return k != rhs.k ? k < rhs.k : b < rhs.b;}
} q[Q];

struct Change {
    int x, y; double k;
    
    bool operator<(const Change &rhs) const {return k < rhs.k;}

    void work() {if (pos[x] < pos[y]) swap(p[pos[x]], p[pos[y]]), swap(pos[x], pos[y]);}
} c[Q];

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> qn;
    for (int i = 1, x, y; i <= n; i++) cin >> x >> y, p[i] = Point(x, y);
    for (int i = 1, z, r; i <= qn; i++) cin >> z >> r, q[i] = Query(z, r, i);
    sort(q + 1, q + qn + 1);

    auto getans = [&](int i, int L, int R) -> void {
        int l = L, r = R, mid, res = L - 1;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (q[i].k * p[mid].x + q[i].b >= p[mid].y) res = mid, l = mid + 1;
            else r = mid - 1;
        }
        ans[q[i].id] += res - L + 1;
    };

    auto solve = [&](int l, int r) -> void {
        int m = 0;
        for (int i = l; i <= r; i++) for (int j = i + 1; j <= r; j++) if (p[i].x != p[j].x) {
            double k = 1.0 * (p[i].y - p[j].y) / (p[i].x - p[j].x);
            if (k <= q[qn].k) c[++m] = Change{i, j, k};
        }
        stable_sort(c + 1, c + m + 1);
        for (int i = 1, j = 1; i <= qn; i++) {
            while (j <= m && c[j].k < q[i].k) c[j++].work();
            getans(i, l, r);
        }
    };

    sort(p + 1, p + n + 1), iota(pos + 1, pos + n + 1, 1);
    int len = sqrt(qn) / 1.5, m = (n + len - 1) / len;
    for (int i = 1; i <= m; i++) solve((i - 1) * len + 1, min(i * len, n));
    for (int i = 1; i <= qn; i++) cout << ans[i] << '\n';
    return 0;
}

C. 挑战哈密顿

咕咕咕咕咕咕………………

posted @ 2024-06-04 10:04  Chy12321  阅读(10)  评论(0编辑  收藏  举报