首页 > 其他分享 >平面图转对偶图专题

平面图转对偶图专题

时间:2024-08-27 10:27:09浏览次数:2  
标签:专题 const int 平面图 pos st define id 对偶

平面图转对偶图

平面图:图且任意两条边不相交
对偶图:将平面图的面抠出来形成的图,注意平面图之外的还有一个面。通过将无向边拆成两条有向边进行完成。
具体来说,拆边之后,考虑用一个vector存下从该点出发的所有边,进行极角排序。然后对每条边在其终点的vector中进行二分找到第一个极角小于等于反边极角的设为\(nxt_i\)。然后找多边形的时候不停地跳\(nxt_i\)直到形成一个闭环。找外面的面的话可以通过计算面积,面积非正的话就是外面的面。

//模板
#include <bits/stdc++.h>
#define PB push_back
#define MP make_pair
#define PII pair<int, int>
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
#define ROF(i, r, l) for (int i = (r); i >= (l); --i)
#define FI first
#define SE second
#define SZ size()
using namespace std;
typedef long long ll;
const int M = 1.2e6 + 5;
const int N = 1.2e6 + 5;
const double eps = 1e-10;
int n, m;
ll s[N];
struct pt {
    int x, y;
    pt() {}
    pt(int _x, int _y) {
        x = _x;
        y = _y;
    }
    pt operator - (const pt &t) const {
        return pt(x - t.x, y - t.y);
    }
    ll operator * (const pt &t) const {
        return 1LL * x * t.y - 1LL * y * t.x;
    }
} p[N];
struct edge {
    int id, u, v;
    double at;
    edge() {}
    edge(int _id, int _u, int _v, double _at) {
        id = _id;
        u = _u;
        v = _v;
        at = _at;
    }
    bool operator < (const edge &t) const {
        return fabs(at - t.at) < eps ? v < t.v : at < t.at;
    }
} e[N];
vector<edge> st[N];
int ed[N][2], pos[N], cnt, tot = 1, nxt[N], rt;
void add(int u, int v) {
    ++tot;
    e[tot] = edge(tot, u, v, atan2(p[v].y - p[u].y, p[v].x - p[u].x));
    st[u].PB(e[tot]);
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    FOR (i, 1, n) {
        cin >> p[i].x >> p[i].y;
    }
    FOR (i, 1, m) {
        cin >> ed[i][0] >> ed[i][1];
        add(ed[i][0], ed[i][1]);
        add(ed[i][1], ed[i][0]);
    }
    FOR (i, 1, n) {
        sort(st[i].begin(), st[i].end());
    }
    FOR (i, 2, tot) {
        int v = e[i].v;
        vector<edge>::iterator p = lower_bound(st[v].begin(), st[v].end(), e[i ^ 1]);
        if (p == st[v].begin()) {
            p = st[v].end();
        }
        --p;
        nxt[i] = (*p).id;
    }
    FOR (i, 2, tot) {
        if (pos[i]) {
            continue;
        }
        pos[i] = pos[nxt[i]] = ++cnt;
        int j = nxt[i];
        while (e[j].v != e[i].u) {
            s[cnt] += (p[e[j].u] - p[e[i].u]) * (p[e[j].v] - p[e[i].u]);
            pos[j = nxt[j]] = cnt;
        }
        if (s[cnt] <= 0) {
            rt = cnt;
        }
    }
    return 0;
}

一些例题

P4001 [ICPC-Beijing 2006] 狼抓兔子

简单的平面图转对偶图优化网络流。首先发现原问题是最小割,然后转化成路径,发现一定是右上方走到左下方,其代价就是经过的边的价值和。直接转对偶图建边就变成了最短路。简单题。

#include <bits/stdc++.h>
#define int long long
#define PB push_back
#define MP make_pair
#define PII pair<int, int>
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
#define ROF(i, r, l) for (int i = (r); i >= (l); --i)
#define FI first
#define SE second
#define SZ size()
using namespace std;
const int N = 2e6 + 5;
const int mod = 998244353;
const int inf = 1e18;
int n, m, S, T, dis[N];
vector<PII> G[N];
inline int id(int x, int y, bool o) {
    if (!x || y >= m) {
        return S;
    }
    if (!y || x >= n) {
        return T;
    }
    return (x - 1) * m * 2 + (y - 1) * 2 + o;
}
struct node {
    int u, d;
    node() {}
    node(int _u, int _d) {
        u = _u;
        d = _d;
    }
    bool operator < (const node &t) const {
        return d > t.d;
    }
};
bool vis[N];
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    S = n * m * 2 + 1;
    T = S + 1;
    FOR (i, 1, n) {
        FOR (j, 1, m - 1) {
            int val;
            cin >> val;
            G[id(i - 1, j, 0)].PB(MP(id(i, j, 1), val));
            G[id(i, j, 1)].PB(MP(id(i - 1, j, 0), val));
        }
    }
    FOR (i, 1, n - 1) {
        FOR (j, 1, m) {
            int val;
            cin >> val;
            G[id(i, j - 1, 1)].PB(MP(id(i, j, 0), val));
            G[id(i, j, 0)].PB(MP(id(i, j - 1, 1), val));
        }
    }
    FOR (i, 1, n - 1) {
        FOR (j, 1, m - 1) {
            int val;
            cin >> val;
            G[id(i, j, 1)].PB(MP(id(i, j, 0), val));
            G[id(i, j, 0)].PB(MP(id(i, j, 1), val));
        }
    }
    memset(dis, 0x3f, sizeof(dis));
    dis[S] = 0;
    priority_queue<node> q;
    q.push(node(S, 0));
    while (!q.empty()) {
        int u = q.top().u;
        q.pop();
        if (vis[u]) {
            continue;
        }
        vis[u] = 1;
        for (auto [v, w]: G[u]) {
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                q.push(node(v, dis[v]));
            }
        }
    }
    cout << dis[T] << '\n';
    return 0;
}

P3249 [HNOI2016] 矿区

进阶题。套路地平面图转对偶图,建图。发现问题转化成一坨连通块求面积和以及面积平方和。建出搜索树统计子树的答案。考虑忽略非树边,对于树边若出发点是儿子则加,否则减去儿子的贡献。发现这样竟然是对的。

#include <bits/stdc++.h>
#define PB push_back
#define MP make_pair
#define PII pair<int, int>
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
#define ROF(i, r, l) for (int i = (r); i >= (l); --i)
#define FI first
#define SE second
#define SZ size()
using namespace std;
typedef long long ll;
const int M = 1.2e6 + 5;
const int N = 1.2e6 + 5;
const double eps = 1e-10;
int n, m, q, poi[M];
ll s[N], s2[N];
bool vis[N];
vector<PII> G[N];
struct pt {
    int x, y;
    pt() {}
    pt(int _x, int _y) {
        x = _x;
        y = _y;
    }
    pt operator - (const pt &t) const {
        return pt(x - t.x, y - t.y);
    }
    ll operator * (const pt &t) const {
        return 1LL * x * t.y - 1LL * y * t.x;
    }
} p[N];
struct edge {
    int id, u, v;
    double at;
    edge() {}
    edge(int _id, int _u, int _v, double _at) {
        id = _id;
        u = _u;
        v = _v;
        at = _at;
    }
    bool operator < (const edge &t) const {
        return fabs(at - t.at) < eps ? v < t.v : at < t.at;
    }
} e[N];
vector<edge> st[N];
int ed[N][2], pos[N], cnt, tot = 1, nxt[N], rt, fa[N];
bool flag[N];
void add(int u, int v) {
    ++tot;
    e[tot] = edge(tot, u, v, atan2(p[v].y - p[u].y, p[v].x - p[u].x));
    st[u].PB(e[tot]);
}
void dfs(int u, int f) {
    s2[u] = s[u] * s[u];
    s[u] <<= 1;
    fa[u] = f;
    vis[u] = 1;
    for (auto [v, w]: G[u]) {
        if (vis[v]) {
            continue;
        } 
        flag[w] = flag[w ^ 1] = 1;
        dfs(v, u);
        s[u] += s[v];
        s2[u] += s2[v];
    }
}
ll ans1, ans2;
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> q;
    FOR (i, 1, n) {
        cin >> p[i].x >> p[i].y;
    }
    FOR (i, 1, m) {
        cin >> ed[i][0] >> ed[i][1];
        add(ed[i][0], ed[i][1]);
        add(ed[i][1], ed[i][0]);
    }
    FOR (i, 1, n) {
        sort(st[i].begin(), st[i].end());
    }
    FOR (i, 2, tot) {
        int v = e[i].v;
        vector<edge>::iterator p = lower_bound(st[v].begin(), st[v].end(), e[i ^ 1]);
        if (p == st[v].begin()) {
            p = st[v].end();
        }
        --p;
        nxt[i] = (*p).id;
    }
    FOR (i, 2, tot) {
        if (pos[i]) {
            continue;
        }
        pos[i] = pos[nxt[i]] = ++cnt;
        int j = nxt[i];
        while (e[j].v != e[i].u) {
            s[cnt] += (p[e[j].u] - p[e[i].u]) * (p[e[j].v] - p[e[i].u]);
            pos[j = nxt[j]] = cnt;
        }
        if (s[cnt] <= 0) {
            rt = cnt;
        }
    }
    FOR (i, 2, tot) {
        G[pos[i]].PB(MP(pos[i ^ 1], i));
    }
    dfs(rt, -1);
    while (q --> 0) {
        int x;
        cin >> x;
        x = (x + ans1) % n + 1;
        FOR (i, 1, x) {
            cin >> poi[i];
            poi[i] = (poi[i] + ans1) % n + 1;            
        }
        poi[x + 1] = poi[1];
        ans1 = ans2 = 0;
        FOR (i, 1, x) {
            int u = poi[i], v = poi[i + 1];
            edge t = edge(0, u, v, atan2(p[v].y - p[u].y, p[v].x - p[u].x));
            vector<edge>::iterator p = lower_bound(st[u].begin(), st[u].end(), t);
            int id = (*p).id;
            if (!flag[id]) {
                continue;
            }
            if (fa[pos[id]] == pos[id ^ 1]) {
                ans1 += s2[pos[id]];
                ans2 += s[pos[id]];
            } else {
                ans1 -= s2[pos[id ^ 1]];
                ans2 -= s[pos[id ^ 1]];
            }
        }
        ll g = __gcd(ans1, ans2);
        ans1 /= g;
        ans2 /= g;
        cout << ans1 << ' ' << ans2 << '\n';
    }
    return 0;
}

P4073 [WC2013] 平面图

平面图转对偶图后发现只要定位了某个点再哪个平面就能通过\(kruscal\)重构树算出答案。关键在于定位。利用题目中的性质,由于题目说了任意两条边不相交,那么两条边开头的大小关系与再任意一个点的大小关系一定相同。那么这样的话考虑扫描线,再起始点加入这条边,在终点删除。用一个set维护当前所有边,两条线的大小关系只要比较开头的大小即可。

#include <bits/stdc++.h>
#define PB push_back
#define MP make_pair
#define PII pair<int, int>
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
#define ROF(i, r, l) for (int i = (r); i >= (l); --i)
#define FI first
#define SE second
#define SZ size()
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
const double eps = 1e-12;
int n, m, pos[N], q;
struct point {
    int x, y;
    point() {}
    point(int _x, int _y) {
        x = _x;
        y = _y;
    }
    bool operator == (const point &t) const {
        return x == t.x && y == t.y;
    }
} p[N];
//点
struct Vector {
    int x, y;
    Vector() {}
    Vector(int _x, int _y) {
        x = _x;
        y = _y;
    }
    Vector(point _x, point _y) {
        y = _y.y - _x.y;
        x = _y.x - _x.x;
    }
    Vector operator - (const Vector &t) const {
        return Vector(x - t.x, y - t.y);
    }
    ll operator * (const Vector &t) const {
        return 1LL * x * t.y - 1LL * y * t.x;
    }
};
//向量
struct line {
    point st;
    double k;
    line() {}
    line(point x, point y) {
        st = x;
        k = 1.0 * (y.y - x.y) / (y.x - x.x);
    }
    line(point x, double _k) {
        st = x;
        k = _k;
    }
    bool operator < (const line &t) const {
        if (st == t.st) {
            return k < t.k;
        }
        int x = max(st.x, t.st.x);
        return st.y + (x - st.x) * k < t.st.y + (x - t.st.x) * t.k;
        //由于保证两条直线不交所以可以直接比较同x下的y值大小,非常巧妙
    }
    bool operator == (const line &t) const {
        return st == t.st && fabs(k - t.k) < eps;
    }
};
//直线二分
struct edge {
    int id, u, v, w;
    double at;
    edge() {}
    edge(int _id, int _u, int _v, int _w, double _at) {
        id = _id;
        u = _u;
        v = _v;
        w = _w;
        at = _at;
    }
    bool operator < (const edge &t) const {
        return fabs(at - t.at) < eps ? v < t.v : at < t.at;
    }
} e[N];
//极角排序
struct K_edge {
    int u, v, w;
    K_edge() {}
    K_edge(int _u, int _v, int _w) {
        u = _u;
        v = _v;
        w = _w;
    }
    bool operator < (const K_edge &t) const {
        return w < t.w;
    }
} ke[N];
//Kruscal重构树
struct scan {
    int ty, pos, id;
    line l;
    scan() {}
    scan(int _t, int _pos, int _id, line _l) {
        ty = _t;
        id = _id;
        l = _l;
        pos = _pos;
    }
    bool operator < (const scan &t) const {
        return pos == t.pos ? ty < t.ty : pos < t.pos;
    }
} sc[N << 1];
//扫描线
vector<edge> h[N];
vector<int> G[N];
int tot = -1, cnt, nxt[N], forb, kcnt, f[N], fa[N][20], val[N], L[N], R[N], timer, ans[N], stot;
set<pair<line, int>> Set;
bool flag[N];
void add(int u, int v, int w) {
    ++tot;
    e[tot] = edge(tot, u, v, w, atan2(p[v].y - p[u].y, p[v].x - p[u].x));
    h[u].PB(e[tot]);
}
int find(int x) {
    return f[x] == x ? x : f[x] = find(f[x]);
}
void dfs(int u, int ft) {
    L[u] = ++timer;
    fa[u][0] = ft;
    FOR (i, 1, 19) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for (int v: G[u]) {
        if (v == ft) {
            continue;
        }
        dfs(v, u);
    }
    R[u] = timer;
}
bool up(int u, int v) {
    return !u || (L[u] <= L[v] && R[v] <= R[u]);
}
int lca(int u, int v) {
    if (up(u, v)) {
        return u;
    }
    if (up(v, u)) {
        return v;
    }
    ROF (i, 19, 0)
        if (!up(fa[u][i], v)) 
            u = fa[u][i];
    return fa[u][0];
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    FOR (i, 1, n) {
        cin >> p[i].x >> p[i].y;
        p[i].x <<= 1;
        p[i].y <<= 1;
    }
    FOR (i, 1, m) {
        int u, v, h;
        cin >> u >> v >> h;
        add(u, v, h);
        add(v, u, h);
    }
    //平面图转对偶图
    FOR (i, 1, n) {
        sort(h[i].begin(), h[i].end());
    }
    FOR (i, 0, tot) {
        int v = e[i].v;
        vector<edge>::iterator it = lower_bound(h[v].begin(), h[v].end(), e[i ^ 1]);
        if (it == h[v].begin()) {
            it = h[v].end();
        }
        --it;
        nxt[i] = (*it).id;
    }
    FOR (i, 0, tot) {
        if (pos[i]) {
            continue;
        }
        pos[i] = pos[nxt[i]] = ++cnt;
        int j = nxt[i];
        ll s = 0;
        while (e[j].v != e[i].u) {
            s += Vector(p[e[j].u], p[e[i].u]) * Vector(p[e[j].v], p[e[i].u]);
            pos[j = nxt[j]] = cnt;
        }
        if (s <= 0) {
            forb = cnt;
        }
    }
    //建树
    FOR (i, 0, tot) {
        if ((i & 1) || pos[i] == forb || pos[i ^ 1] == forb) {
            continue;
        }
        ke[++kcnt] = K_edge(pos[i], pos[i ^ 1], e[i].w);
    }
    sort(ke + 1, ke + kcnt + 1);
    FOR (i, 1, cnt) {
        f[i] = i;
    }
    int all = cnt;
    FOR (i, 1, kcnt) {
        int x = find(ke[i].u), y = find(ke[i].v);
        if (x == y) {
            continue;
        }
        ++all;
        f[x] = all;
        f[y] = all;
        f[all] = all;
        fa[x][0] = fa[y][0] = all;
        val[all] = ke[i].w;
        G[all].PB(x);
        G[all].PB(y);
    }
    dfs(all, 0);
    FOR (i, 0, tot) {
        if (p[e[i].u].x < p[e[i].v].x) {
            sc[++stot] = scan(2, p[e[i].u].x, pos[i ^ 1], line(p[e[i].u], p[e[i].v]));
            sc[++stot] = scan(1, p[e[i].v].x, pos[i ^ 1], line(p[e[i].u], p[e[i].v]));
        }
    }
    cin >> q;
    FOR (i, 1, q) {
        double ax, bx, ay, by;
        cin >> ax >> ay >> bx >> by;
        ax *= 2;
        ay *= 2;
        bx *= 2;
        by *= 2;
        sc[++stot] = scan(3, ax, i, line(point(ax, ay), 0.0));
        sc[++stot] = scan(3, bx, i, line(point(bx, by), 0.0));
    }
    sort(sc + 1, sc + stot + 1);
    FOR (i, 1, stot) {
        if (sc[i].ty == 2) {
            Set.insert(MP(sc[i].l, sc[i].id));
        } else if (sc[i].ty == 1) {
            Set.erase(MP(sc[i].l, sc[i].id));
        } else {
            if (flag[sc[i].id]) {
                continue;
            }
            set<pair<line, int>>::iterator it = Set.upper_bound(MP(sc[i].l, 0));
            if (it == Set.end() || it == Set.begin()) {
                flag[sc[i].id] = 1;
                continue;
            }
            if (!ans[sc[i].id]) {
                ans[sc[i].id] = (*it).SE;
            } else {
                ans[sc[i].id] = val[lca((*it).SE, ans[sc[i].id])];
            }
        }
    }
    FOR (i, 1, q) {
        if (flag[i]) {
            cout << -1 << '\n';
        } else {
            cout << ans[i] << '\n';
        }
    }
    return 0;
}

标签:专题,const,int,平面图,pos,st,define,id,对偶
From: https://www.cnblogs.com/IANYE/p/18382058

相关文章

  • 【K8s】专题十二(3):Kubernetes 存储之 PersistentVolumeClaim
    本文内容均来自个人笔记并重新梳理,如有错误欢迎指正!如果对您有帮助,烦请点赞、关注、转发、订阅专栏!专栏订阅入口Linux专栏 | Docker专栏 | Kubernetes专栏往期精彩文章【Docker】(全网首发)KylinV10下MySQL容器内存占用异常的解决方法【Docker】(全网首发)Kyli......
  • 基于SpringBoot+Vue+uniapp的安康学院新型冠状病毒肺炎疫情防控专题网站的详细设计和
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 【专题】2024数智医疗服务时代营销机遇洞察报告合集PDF分享(附原数据表)
    原文链接: https://tecdat.cn/?p=37466如今,人工智能、大数据、物联网等众多智能技术持续且深入地在医药产业中得到应用。这不仅极大地增强了医药产业的创新能力,显著提高了医疗服务的质量与效率,还有力地促进了从预防、诊断、治疗到康复的全链条数字化转型,使市场效率得到大幅提升。......
  • C/C++语言基础--指针三大专题详解3,完结篇(包括指针做函数参数,函数指针,回调函数,左右法
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言指针是C/C++的灵魂,和内存地址相关联,运行的时候速度快,但是同时也有很多细节和规范要注意的,毕竟内存泄漏是很恐怖的指针打算分三篇文章进行讲解,本专题是三,完结篇,介绍了指针做函数参数,函数指针,回调函数,左右法则解决复......
  • 【专题】2023-2024中国游戏企业研发竞争力报告合集PDF分享(附原数据表)
    原文链接: https://tecdat.cn/?p=37447 在当今的数字时代,游戏产业已然成为经济与文化领域中一股不可忽视的重要力量。2023年,中国自研游戏市场更是呈现出一片繁荣且复杂的景象,实际销售收入达到了令人瞩目的2563.8亿元,同比增长15.3%,不仅刷新历史纪录,还彰显出其强大的市场活力......
  • C/C++语言基础--指针三大专题详解2(指针与数组关系,动态内存分配,代码均可)
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言指针是C/C++的灵魂,和内存地址相关联,运行的时候速度快,但是同时也有很多细节和规范要注意的,毕竟内存泄漏是很恐怖的指针打算分三篇文章进行讲解,本专题是二,介绍了指针和数组的关系、动态内存如何分配和释放等问题专题......
  • ACM算法——数学专题
    一、素数1、欧拉筛时间复杂度:\(O(n)\)constexprintN=1E6;std::vector<int>primes;std::vector<bool>st;voidinit(intn){st.assign(n+1,false);primes.clear();for(inti=2;i<=n;i++){if(!st[i]){pri......
  • 专题:C语言操作符详解
    ⽬录1.操作符的分类2.⼆进制和进制转换3.原码、反码、补码4.移位操作符5.位操作符:&、|、^、~6.单⽬操作符7.逗号表达式8.下标访问[]、函数调⽤()9.结构成员访问操作符10.操作符的属性:优先级、结合性11.表达式求值1.操作符的分类•算术操作符:+、-......
  • 专题1:树莓派Pico引脚功能详解
    1主要功能RaspberryPiPico一共有40个针脚。这是RaspberryPiPico官方提供的引脚图,应该有很多人看到上面的图标都是两眼一摸黑,接下来,我将从每种颜色分类来讲述每个引脚的功能2Power类引脚Power,顾名思义就是电源的意思。这种引脚一共有三个,Power类引脚是为接到Pico上......
  • springboot电竞专题网站的设计与实现-附源码641314
    摘 要近年来,随着移动互联网的快速发展,电子商务越来越受到网民们的欢迎,电子商务对国家经济的发展也起着越来越重要的作用。简单的流程、便捷可靠的支付方式、快捷畅通的物流快递、安全的信息保护都使得电子商务越来越赢得网民们的青睐。现今,大量的计算机技术应用于商业领域,......