「Fortuna OJ」Jan 10 省选组 – 解题报告

在纪中,不铺好蚊帐就等着第二天比赛 GG。

kal0rona

A – 鸡腿の梦境

计算几何套路题,所以不会。

其实考试的时候看得出来是判断点是否在多边形内,但是真的不会写然后就爆零了。主要原理在于跨立试验和判奇权环。

// A.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 330;

int n, head[MAX_N], current;
double ri[MAX_N];
bool vis[MAX_N][2];

struct edge
{
    int to, nxt, weight;
} edges[MAX_N * MAX_N << 1];

void addpath(int src, int dst, int weight)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    edges[current].weight = weight, head[src] = current++;
}

struct point
{
    double x, y;
} pts[MAX_N];

double pow2(double x) { return x * x; }

double getDist(point &a, point &b) { return sqrt(pow2(a.x - b.x) + pow2(a.y - b.y)); }

bool judge(point &a, point &b, point &c, point &d)
{
    double u, v, w, z;
    u = (c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y);
    v = (d.x - a.x) * (b.y - a.y) - (b.x - a.x) * (d.y - a.y);
    w = (a.x - c.x) * (d.y - c.y) - (d.x - c.x) * (a.y - c.y);
    z = (b.x - c.x) * (d.y - c.y) - (d.x - c.x) * (b.y - c.y);
    return (u * v <= 0.00001 && w * z <= 0.00001);
}

void dfs(int u, int fa, int opt)
{
    if (vis[u][opt])
        return;
    vis[u][opt] = true;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
            dfs(edges[i].to, u, opt ^ edges[i].weight);
}

int main()
{
    point remote_point = {9024.0, 6002.0};
    while (scanf("%d", &n) != EOF)
    {
        memset(head, -1, sizeof(head)), current = 0;
        for (int i = 1; i <= n + 1; i++)
            scanf("%lf%lf%lf", &pts[i].x, &pts[i].y, &ri[i]);
        for (int i = 1; i <= n; i++)
            ri[i] += ri[n + 1];
        for (int i = 1; i <= n; i++)
            for (int j = i + 1; j <= n; j++)
                if (getDist(pts[i], pts[j]) < ri[i] + ri[j])
                    if (judge(pts[i], pts[j], pts[n + 1], remote_point))
                        addpath(i, j, 1), addpath(j, i, 1);
                    else
                        addpath(i, j, 0), addpath(j, i, 0);
        bool flag = true;
        for (int i = 1; i <= n; i++)
        {
            memset(vis, false, sizeof(vis));
            dfs(i, 0, 0);
            for (int j = 1; j <= n; j++)
                if (vis[j][0] && vis[j][1])
                {
                    flag = false;
                    break;
                }
            if (flag == false)
                break;
        }
        puts(flag ? "YES" : "NO");
    }
    return 0;
}

B – 鸡腿の花园

我恨卡哈希。把哈希函数设置的奇怪一点就特么过了,害。

// B.cpp
#include <bits/stdc++.h>
#define ull unsigned long long

using namespace std;

const int MAX_N = 2e5 + 200, bitnum = 133, mod1 = 1e9 + 7, mod2 = 1e9 + 9, bitlft = 23333, bitrig = 998443;

struct hash_val
{
    int val1, val2;

    hash_val operator+(const int &ch)
    {
        hash_val ret = *this;
        ret.val1 = (1LL * ret.val1 + mod1 + ch) % mod1;
        ret.val2 = (1LL * ret.val2 + mod2 + ch) % mod2;
        return ret;
    }

    hash_val operator+(const hash_val &ch)
    {
        hash_val ret = *this;
        ret.val1 = (1LL * ret.val1 + mod1 + ch.val1) % mod1;
        ret.val2 = (1LL * ret.val2 + mod2 + ch.val2) % mod2;
        return ret;
    }

    hash_val operator-(const hash_val &rhs)
    {
        hash_val ret = *this;
        ret.val1 = (0LL + ret.val1 + mod1 - rhs.val1) % mod1;
        ret.val2 = (0LL + ret.val2 + mod2 - rhs.val2) % mod2;
        return ret;
    }

    hash_val operator*(const int &rhs)
    {
        hash_val ret = *this;
        ret.val1 = 1LL * ret.val1 * rhs % mod1;
        ret.val2 = 1LL * ret.val2 * rhs % mod2;
        return ret;
    }

    hash_val operator*(const hash_val &rhs)
    {
        hash_val ret = *this;
        ret.val1 = 1LL * ret.val1 * rhs.val1 % mod1;
        ret.val2 = 1LL * ret.val2 * rhs.val2 % mod2;
        return ret;
    }

    bool operator==(const hash_val &target) const { return val1 == target.val1 && val2 == target.val2; }

    bool operator<(const hash_val &target) const { return val1 < target.val1 || (val1 == target.val1 && val2 < target.val2); }
};

int n, m, ch[MAX_N][2], siz[MAX_N], fa[MAX_N];
hash_val nodes[MAX_N], bpow[MAX_N];
map<hash_val, int> mp;
long long ans = 0;

void dfs(int u, int opt)
{
    siz[u] = 1;
    if (ch[u][0] != -1)
        dfs(ch[u][0], opt), siz[u] += siz[ch[u][0]];
    else
        ch[u][0] = 0;
    if (ch[u][1] != -1)
        dfs(ch[u][1], opt), siz[u] += siz[ch[u][1]];
    else
        ch[u][1] = 0;

    hash_val curt = hash_val{0, 0};
    curt = nodes[ch[u][0]] * nodes[ch[u][0]] * bitlft + nodes[ch[u][1]] * bitrig + (siz[u] ^ siz[ch[u][1]]);
    nodes[u] = curt;
    if (opt == 0)
        mp[nodes[u]]++;
    else
        ans += mp[nodes[u]];
}

void setFather(int x, int fat) { x > 0 ? fa[x] = fat : 0; }

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &ch[i][0], &ch[i][1]), setFather(ch[i][0], i), setFather(ch[i][1], i);
    bpow[0] = hash_val{1, 1};
    for (int i = 1; i <= max(n, m); i++)
        bpow[i] = bpow[i - 1] * bitnum;
    for (int i = n + 1; i <= n + m; i++)
    {
        scanf("%d%d", &ch[i][0], &ch[i][1]);
        if (ch[i][0] != -1)
            ch[i][0] += n;
        if (ch[i][1] != -1)
            ch[i][1] += n;
        setFather(ch[i][0], i), setFather(ch[i][1], i);
    }
    int root1, root2;
    for (int i = 1; i <= n; i++)
        if (fa[i] == 0)
            root1 = i;
    for (int i = n + 1; i <= n + m; i++)
        if (fa[i] == 0)
            root2 = i;
    dfs(root1, 0), dfs(root2, 1), printf("%lld\n", ans);
    return 0;
}

C – 鸡腿の游戏

我特么是中了邪,一直写缩点,最后发现跟 BLO 很像。

首先,按照割点的套路去写个 Tarjan。当该点为割点时,我们就需要计算其在搜索树上的 LCA 为 \(u\) 的点对方案。跟 BLO 几乎一样吧(存疑),然后就解决了(我真是个sb)。

// C.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 5e4 + 200, MAX_E = 1e5 + 200;
typedef long long ll;

int head[MAX_N], current, n, m, dfn[MAX_N], low[MAX_N], ptot, tot[MAX_N];
ll ans[MAX_N];

struct edge
{
    int to, nxt;
} edges[MAX_E << 1];

void addpath(int src, int dst)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    head[src] = current++;
}

void tarjan(int u, int fa)
{
    ll cnt = 0;
    dfn[u] = low[u] = ++ptot;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
            if (dfn[edges[i].to] == 0)
            {
                tarjan(edges[i].to, u), low[u] = min(low[u], low[edges[i].to]);
                tot[u] += tot[edges[i].to];
                if (low[edges[i].to] >= dfn[u])
                {
                    ans[u] -= cnt * tot[edges[i].to], ans[u] += 1LL * (n - tot[edges[i].to] - 1) * tot[edges[i].to];
                    cnt += tot[edges[i].to];
                }
            }
            else
                low[u] = min(low[u], dfn[edges[i].to]);
    ans[u] += n - 1, tot[u]++;
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    for (int i = 1, u, v; i <= m; i++)
    {
        scanf("%d%d", &u, &v);
        if (u != v)
            addpath(u, v), addpath(v, u);
    }
    for (int i = 1; i <= n; i++)
        if (dfn[i] == 0)
            tarjan(i, 0);
    for (int i = 1; i <= n; i++)
        printf("%lld\n", ans[i]);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *