POJ 1182 食物链 并查集+偏移向量

引言:

一般的并查集都是把同一类的点放在一棵树上。但是这道题给出的是一些点的相对关系,就不能按照这样的规则建树了。应该把能够确定相对关系的点放到一棵树上。每个节点除了自身的值之外,还应该有一个域,或者说是偏移量,它表示的是这个点和其父节点之间的关系。所以当我们要改变一个节点的父节点时,还要同时更新这个点和父节点的关系。

什么时候一个点的父节点会改变呢?

1. 路径压缩

2.两棵树合并

正文:

题意很简单,不再赘述。大致解题思路就是:我们在输入的过程中不断的维护并查集,如果输入的两个动物在同一个集合内,也就是他们的关系我们已经确定了。那么就把我们确定的关系和输入的关系比较,相等则为真,反之为假。如果两个动物不在一个集合内,即他们的关系我们还不确定,所以这句话肯定是真。同时我们还要把两个动物所在的集合合并。假设x是y的父亲,如果y的偏移量是0,代表x和y是同类,如果等于1,代表y被x吃,如果为2,代表x吃y。输入的值是d,x,y。d-1就是x和y的关系,d-1==0,y和x是同类。d-1==1, y被x吃。

上文中说过了,路径压缩的时候父节点会变,所以我们要改变当前节点的偏移量。因为是递归压缩,所以在把当前节点作为根节点的儿子时,它之前的父亲已经成为根节点的儿子了。假设原来root->a->x->y,压缩之后就成了root->a, root->x, root->y。我们知道了x->y时y的偏移量,root->x时x的偏移量,该怎么求root->y的偏移量呢?用y.relation表示y的偏移量。则y.relation = (x.relation + y.relation) %3。

怎么算的呢?这个也就是当我们已知爷爷和父亲的关系a,父亲和儿子的关系b,求爷爷和儿子的关系c。我们可以穷举出所有可能,然后总结出上面的式子。

另外在树合并的时候也要改变偏移量。假设输入x,y。我们发现他们不在同一个集合。x的根是rootx,y的根节点是rooty。我们把rootx作为rooty的父节点。所以同时我们要更新rooty.relation。因为在之前我们运行过一次find_root,所以也进行了路径压缩。所以现在x,y到他们根的距离只有1,即根就是x,y的父亲。

另外补充一点,假如x作为y的父亲时,y的偏移量是a,那么y作为x的父亲时,x的偏移量就是(3 - a)%3。

现在我们来计算rooty.relation。先把x作为y的父亲,y的偏移量是d-1,y作为rooty的父亲,rooty的偏移量是, (3 - y.relation) % 3, 所以x当rooty父亲时,rooty的偏移量就是

((d-1) + (3 - y.relation) )%3,这里运用了同余公式。我们已知了rootx作为x父亲时,x的偏移量,所以我们就可以计算出,rootx作为rooty父亲时,rooty的偏移量是

(x.relation + (d - 1) + (3 - y.relation))%3,这里运用了同余公式。

具体代码如下:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <complex>
#include <cstring>
#include <utility>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <new>
#include <set>
#include <map>

using namespace std;

const int maxn = 50000 + 5;
const int INF = 0x3f3f3f3f;
int d;
struct Animal
{
    int id;
    int parent;
    int relation;
};
Animal a[maxn];

int find_root(int x)
{
    if (a[x].parent == x)
        return x;
    else
    {
        int temp = a[x].parent;
        a[x].parent = find_root(temp);
        a[x].relation = (a[x].relation + a[temp].relation) % 3;
        return a[x].parent;
    }
}

void unite(int x, int y)
{
    int rx = find_root(x);
    int ry = find_root(y);
    if (rx != ry)
    {
        a[ry].parent = rx;
        a[ry].relation = ((3 - a[y].relation) + (d - 1) + a[x].relation) % 3;
    }
}

void initial()
{
    for (int i = 0; i < maxn; i++)
    {
        a[i].id = i;
        a[i].parent = i;
        a[i].relation = 0;
    }
}

int main()
{

    //freopen("1.txt", "r", stdin);
    int n, k;
    scanf("%d%d", &n, &k);
    int ans = 0;
    initial();
    for (int i = 0; i < k; i++)
    {
        int A1, A2;
        scanf("%d%d%d", &d, &A1, &A2);
        if (A1 > n || A2 > n)
            ans++;
        else if (A1 == A2 && d != 1)
            ans++;
        else if (find_root(A1) != find_root(A2))//不在同一个集合
            unite(A1, A2);
        else
        {
            switch(d)
            {
                case 1:
                {
                    if (a[A1].relation != a[A2].relation)
                    //因为之前进行过路径压缩,所以A1,A2的父亲节点都是根节点
                    //如果A1,A2是同类,那么他们的relation应该是相等的
                        ans++;
                    break;
                }
                case 2:
                {
                    if(((a[A2].relation + 3 - a[A1].relation) % 3 ) != 1)
                    //稍微推导一下就知道这是代表A1作为A2父亲时,A2的偏移量
                        ans++;
                    break;
                }
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这个问题属于技术问题。以下是一个简单的Python模拟登录POJ提交代码并抓取评测结果的代码示例: ```python import requests # 登录POJ,获取cookie def login(username, password): s = requests.Session() login_url = "http://poj.org/login" login_data = { "user_id1": username, "password1": password, "B1": "login", "url": "/" } s.post(login_url, data=login_data) return s # 提交代码 def submit_code(s, problem_id, language, source_code): submit_url = "http://poj.org/submit" submit_data = { "problem_id": problem_id, "language": language, "source": source_code } s.post(submit_url, data=submit_data) # 获取评测结果 def get_result(s, run_id): status_url = "http://poj.org/status" params = { "user_id": "", "result": "", "language": "", "top": run_id } r = s.get(status_url, params=params) table_start = r.text.find("<table cellpadding=0 cellspacing=0 border=0 width=100%>") table_end = r.text.find("</table>", table_start) table_html = r.text[table_start:table_end + 8] return table_html # 使用示例 username = "your_username" password = "your_password" problem_id = "1000" language = "G++" source_code = """ #include <iostream> using namespace std; int main() { int a, b; cin >> a >> b; cout << a + b << endl; return 0; } """ s = login(username, password) submit_code(s, problem_id, language, source_code) table_html = get_result(s, "12345678") # 替换成实际提交的run id print(table_html) ``` 其中,`login`函数模拟登录POJ并返回一个`Session`对象,`submit_code`函数提交代码,`get_result`函数获取评测结果。你可以根据实际需要修改代码中的`username`、`password`、`problem_id`、`language`和`source_code`等参数,并替换`get_result`函数中的`run_id`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值