5.10 省选模拟赛 网格 平面图欧拉定理 并查集

LINK:网格

avatar
avatar

搞了一下午 总算把这个平面欧拉定理给搞懂了。

大体上是欧拉定理 的一些定义难以理解。

关于证明我也不太会。题解写的那叫一个抽象 最终看std明白了题解的意思。

平面图欧拉定理:定义 V是点数 E为边数 F为区域数 C为连通块个数

那么存在 V+F-E=C+1;的关系。

其中 关于边数E的则认为是一个八联通的边 对于区域则需要百度一下 当然三角形也算是一个区域 (广大的UOJ群友就是牛.

对于前者 在定理中就是定义 在这道题中则容易想到边的关系显然包含斜着的边 后者同理。

考虑这道题的暴力 第一子任务是暴力bfs 不过遇到边界就加上一些特殊操作。

对于第二个子任务 这个我没实现 想到一个复杂度为n^3的东西 每次便利整张图 然后利用 dfs树的性质来判断区域。不过实现较麻烦 可能还通过不了。

对于第三个子任务 离线之后容易想到利用并查集判断联通性 每次加边判断连通块的增减性即可。

对于第四个子任务 用第三个就不太能写了 关键是整张图的连通块个数不能得到 且后续操作也完成不了。

code: score 35

const int MAXN = 100010, maxn = 210, N = 1001010, MAX = 1010;
;
const int dx[4] = { 0, 0, 1, -1 };
const int dy[4] = { 1, -1, 0, 0 };
int T, Q, flag1, ans, id, flag, flag2, root;
struct wy {
    int x, y;
} t[MAXN];
int b[maxn][maxn], vis[MAX][MAX];
int f[N], mark[N], ans1[MAXN];
queue<pii> q;
inline void bfs(int s1, int s2) {
    q.push(mk(s1, s2));
    while (q.size()) {
        int x = q.front().F;
        int y = q.front().S;
        q.pop();
        for (int i = 0; i < 4; ++i) {
            int xx = dx[i] + x;
            int yy = dy[i] + y;
            if (xx < 0 || yy<0 | xx> 200 || yy > 200) {
                flag = 1;
                continue;
            }
            if (vis[xx][yy])
                continue;
            if (b[xx][yy] == id)
                continue;
            b[xx][yy] = id;
            q.push(mk(xx, yy));
        }
    }
}
inline void sol1() {
    rep(1, Q, i) {
        int x = t[i].x ^ (T * ans);
        int y = t[i].y ^ (T * ans);
        x += 100;
        y += 100;
        vis[x][y] = 1;
        int cnt = 1;
        ++id;
        rep(0, 200, i) {
            rep(0, 200, j) {
                if (vis[i][j])
                    continue;
                if (b[i][j] == id)
                    continue;
                flag = 0;
                b[i][j] = id;
                bfs(i, j);
                if (!flag)
                    ++cnt;
            }
        }
        ans = cnt;
        put(ans);
    }
    exit(0);
}
inline int getfather(int x) { return x == f[x] ? x : f[x] = getfather(f[x]); }
inline void merge(int x, int y) {
    int xx = getfather(x);
    int yy = getfather(y);
    f[xx] = yy;
    return;
}
inline void sol2() {
    root = 1001001;
    f[root] = root;
    rep(1, Q, i) {
        t[i].x += 500;
        t[i].y += 500;
        int x = t[i].x;
        int y = t[i].y;
        vis[x][y] = 1;
    }
    rep(0, 1000, i) rep(0, 1000, j) f[i * 1000 + j] = i * 1000 + j;
    rep(0, 1000, i) rep(0, 1000, j) {
        if (vis[i][j])
            continue;
        int id1 = i * 1000 + j;
        rep(0, 3, k) {
            int xx = dx[k] + i;
            int yy = dy[k] + j;
            if (xx < 0 || yy < 0 || xx > 1000 || yy > 1000)
                merge(root, id1);
            else {
                if (vis[xx][yy])
                    continue;
                merge(xx * 1000 + yy, id1);
            }
        }
    }
    rep(0, 1000, i) rep(0, 1000, j) {
        if (vis[i][j])
            continue;
        int xx = getfather(i * 1000 + j);
        if (!mark[xx])
            ++ans;
        mark[xx] = 1;
    }
    fep(Q, 1, i) {
        ans1[i] = ans;
        int x = t[i].x;
        int y = t[i].y;
        int id1 = x * 1000 + y;
        f[id1] = id1;
        vis[x][y] = 0;
        ++ans;
        rep(0, 3, k) {
            int xx = dx[k] + x;
            int yy = dy[k] + y;
            if (xx < 0 || yy < 0 || xx > 1000 || yy > 1000) {
                if (getfather(id1) != getfather(root))
                    --ans;
                merge(id1, root);
            } else {
                if (vis[xx][yy])
                    continue;
                if (getfather(id1) != getfather(xx * 1000 + yy))
                    --ans;
                // cout<<getfather(xx*1000+yy)<<' '<<getfather(id1)<<endl;
                merge(id1, xx * 1000 + yy);
            }
        }
        // put(ans);
    }
    rep(1, Q, i) put(ans1[i]);
    exit(0);
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    get(Q);
    get(T);
    rep(1, Q, i) {
        int get(x), get(y);
        t[i] = (wy){ x, y };
        if (abs(x) > 100 && abs(y) > 100)
            flag1 = 1;
        if (abs(x) > 500 && abs(y) > 500)
            flag2 = 1;
    }
    if (!flag1)
        sol1();
    if (!flag2 && !T)
        sol2();
    return 0;
}

正解就是利用欧拉定理来做。

容易想到最后答案求得其实是F-实心区域个数。

而容易发现实心的区域个数一定是为三角形的区域。

所以 我们仍要多维护一个三角形区域个数 设为O.

V直接维护 E也直接维护 C利用并查集维护 关键是对于O的维护 加入一个点的时候 这个点会造成贡献无疑是和其8联通的正方形。

暴力枚举12个三角形就可以维护。

更简便的方法是 枚举每个三角形的右下角 然后再枚举右上方极小正方形来判断。

值得注意的是 可能加上之前的三角形区域了 先减掉之前的即可。

const int MAXN = 100010;
const int dx[8] = { -1, -1, -1, 0, 0, 1, 1, 1 };
const int dy[8] = { 0, -1, 1, 1, -1, -1, 0, 1 };
int Q, T;
int ans, f[MAXN];
int V, E, C, O;
map<ll, int> H;
inline int getfather(int x) { return x == f[x] ? x : f[x] = getfather(f[x]); }
inline ll ha(int x, int y) {
    x += INF, y += INF;
    return (ll)x << 31 | y;
}
inline int qy(int x, int y) {
    int cnt = 0;
    if (H.find(ha(x, y)) != H.end())
        ++cnt;
    if (H.find(ha(x + 1, y)) != H.end())
        ++cnt;
    if (H.find(ha(x, y + 1)) != H.end())
        ++cnt;
    if (H.find(ha(x + 1, y + 1)) != H.end())
        ++cnt;
    if (cnt == 3)
        return 1;
    if (cnt == 4)
        return 3;
    return 0;
}
int main() {
    // freopen("1.in","r",stdin);
    get(Q);
    get(T);
    rep(1, Q, i) {
        int get(x) ^ (T * ans), get(y) ^ (T * ans);
        ll ww = ha(x, y);
        ++V;
        ++C;
        O -= qy(x, y);
        O -= qy(x - 1, y);
        O -= qy(x, y - 1);
        O -= qy(x - 1, y - 1);
        H[ww] = i;
        f[i] = i;
        O += qy(x, y);
        O += qy(x - 1, y);
        O += qy(x, y - 1);
        O += qy(x - 1, y - 1);
        for (int j = 0; j < 8; ++j) {
            int xx = dx[j] + x;
            int yy = dy[j] + y;
            if (H.find(ha(xx, yy)) == H.end())
                continue;
            int ww = H[ha(xx, yy)];
            int w1 = getfather(i);
            int w2 = getfather(ww);
            ++E;
            if (w1 != w2) {
                f[w1] = w2;
                --C;
            }
        }
        put(ans = (E + C + 1 - V - O));
        // cout<<E<<' '<<V<<' '<<C<<' '<<O<<'
';
    }
    return 0;
}
原文地址:https://www.cnblogs.com/chdy/p/12871067.html