BZOJ 1039. [ZJOI2008]无序运动Movement

 

平移、旋转、放缩对两个相似三角形没有影响,那么一个长度为 $n$ 的轨迹就可以描述为 $n-2$ 个三角形,每个三角形就用相邻两边长来描述,还得加上第二条线段在第一条线段的逆时针还是顺时针方向,因为如果不加这个就会出现翻不翻转带来的影响,然后就变成了字符串匹配了。不过由于字符集很大,得用 map 来存边。然后翻转一下再做一遍。
如果一个轨迹共线的话,翻转后他会被重新算一遍,所以要除以 $2$。
如果一个轨迹长度小于 $3$ 的话, 他就能匹配上所有长度相等的子串。

BZOJ 1039. [ZJOI2008]无序运动Movement
#include <bits/stdc++.h>

namespace IO {
    char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = '\n';
    int p, p3 = -1;
    void read() {}
    void print() {}
    inline int getc() {
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void flush() {
        fwrite(buf2, 1, p3 + 1, stdout), p3 = -1;
    }
    template <typename T, typename... T2>
    inline void read(T &x, T2 &... oth) {
        T f = 1; x = 0;
        char ch = getc();
        while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); }
        while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); }
        x *= f;
        read(oth...);
    }
    template <typename T, typename... T2>
    inline void print(T x, T2... oth) {
        if (p3 > 1 << 20) flush();
        if (x < 0) buf2[++p3] = 45, x = -x;
        do {
            a[++p] = x % 10 + 48;
        } while (x /= 10);
        do {
            buf2[++p3] = a[p];
        } while (--p);
        buf2[++p3] = hh;
        print(oth...);
    }
}

struct P {
    int x, y;
    void read() { IO::read(x, y); }
    void print() { printf("%d %d\n", x, y); }
    P() {}
    P(int x, int y): x(x), y(y) {}
    P operator + (const P &p) const { return P(x + p.x, y + p.y); }
    P operator - (const P &p) const { return P(x - p.x, y - p.y); }
    int det(const P &p) const { return x * p.y - y * p.x; }
    int abs2() { return x * x + y * y; }
};

struct Node {
    int a, b, c, dir;
    bool operator < (const Node &p) const {
        if (a != p.a) return a < p.a;
        if (b != p.b) return b < p.b;
        if (c != p.c) return c < p.c;
        return dir < p.dir;
    }
    bool operator == (const Node &p) const {
        return !(*this < p) && !(p < *this);
    }
};

const int N = 2e5 + 7;
int n, m, par[N], tol, ans[N];
std::vector<P> point[N], all;
std::vector<int> flag[N];
std::map<Node, int> mp[N];

int gcd(int a, int b) {
    while (b) {
        a %= b;
        std::swap(a, b);
    }
    return a;
}

Node getnode(const P &A, const P &B, const P &C) {
    int lena = (B - A).abs2(), lenb = (B - C).abs2(), lenc = (A - C).abs2();
    int g = gcd(lena, gcd(lenb, lenc));
    lena /= g, lenb /= g, lenc /= g;
    int crs = 0;
    if ((B - A).det(C - B) > 0) crs = 1;
    else if ((B - A).det(C - B) < 0) crs = -1;
    return {lena, lenb, lenc, crs};
}


void done(std::vector<P> vec, int id) {
    if (vec.size() < 3) {
        ans[id] = n - vec.size() + 1;
        return;
    }
    par[id] = 1;
    int rt = 0;
    for (int i = 0; i < vec.size() - 2; i++) {
        Node o = getnode(vec[i], vec[i + 1], vec[i + 2]);
        if (o.dir) par[id] = 0;
        auto it = mp[rt].find(o);
        if (it == mp[rt].end()) {
            mp[rt][o] = ++tol;
            rt = tol;
        } else {
            rt = it->second;
        }
    }
    flag[rt].push_back(id);
}

int fail[N], last[N], cnt[N];

void build() {
    std::queue<int> que;
    for (auto it: mp[0])
        que.push(it.second);
    while (!que.empty()) {
        int u = que.front(); que.pop();
        for (auto it: mp[u]) {
            Node cur_node = it.first;
            int f = fail[u], v = it.second;
            for (; f && mp[f].find(cur_node) == mp[f].end(); f = fail[f]);
            if (mp[f].find(cur_node) != mp[f].end())
                f = mp[f][cur_node];
            fail[v] = f;
            last[v] = flag[fail[v]].empty() ? last[fail[v]] : fail[v];
            que.push(v);
        }
    }
}

void solve(const std::vector<P> &vec) {
    int rt = 0;
    for (int i = 0; i < n - 2; i++) {
        Node node = getnode(vec[i], vec[i + 1], vec[i + 2]);
        for (; rt && mp[rt].find(node) == mp[rt].end(); rt = fail[rt]);
        if (mp[rt].find(node) != mp[rt].end())
            rt = mp[rt][node];
        for (int j = rt; j; j = last[j])
            ++cnt[j];
    }
}

int main() {
    IO::read(n, m);
    for (int i = 1; i <= m; i++) {
        int k;
        IO::read(k);
        point[i].resize(k);
        for (int j = 0; j < k; j++) 
            point[i][j].read();
        done(point[i], i);
    }
    build();
    all.resize(n);
    for (int i = 0; i < n; i++)
        all[i].read();
    solve(all);
    for (int i = 0; i < n; i++)
        all[i].y *= -1;
    solve(all);
    for (int i = 1; i <= tol; i++)
        for (int u: flag[i])
            ans[u] += cnt[i] / (par[u] + 1);
    for (int i = 1; i <= m; i++)
        IO::print(ans[i]);
    IO::flush();
    return 0;
}
View Code

 

上一篇:洛谷P2590 [ZJOI2008]树的统计


下一篇:P2590 [ZJOI2008]树的统计