题解51nod1515——明辨是非

前提

在这道题老师讲过之后,再考时,我还是WA了ヽ(*。>Д<)o゜果然,我还是好菜啊~%?…,# *‘☆&℃$︿★?

谢谢Jack_Pei dalao的帮忙。~~O(∩_∩)O~~

正文

在我这台机子上,直接由网址上到题目会出现 NOFIND404 所以这一次希望看题解的你可以自己动手丰衣足食去找到这道题

首先,这题一看要维护关系,且数据范围不小时,就想到了并查集&&离散化

相等关系具有传递性,即a==b&&b==c等价于a==c。所以当我们在维护相等关系时,我们会使用并查集。每次找到根节点,再合并一下,就相当于维护了相等关系。即我是你爸爸的儿子,你也是你的爸爸的儿子,我们自然地位也就相等——都是爸爸的儿子

但,由某本数学书,我们可以知道,a!=b&&b!=c不等价于a!=c。即你是你爸爸的儿子,我不是你爸爸的儿子,可是我却是你爸爸的女儿,但我们地位还是一样的——都是爸爸的孩子。但是a==b&&b!=c等价于a!=c。所以此时我们就希望去给每一个并查集中的节点维护这一个不等的关系,表示这些数和它不等。而其既可以合并,又可以快捷查询,简便操作,显然vector,,set中选择一个(map常数大,用它弊大于利,优点没有其他更加明显。优先队列无法做到便捷的插入删除与寻找)。而最后应该用set。

理由如下

Vector动态开四倍空间,相当于在最坏的情况下,一半的节点上,你明明只占用了五万多一点的大小,动态内存却开到二十万,且中间有许多重复的数字。你会MLE(机房某位dalao曾现身说法过)

Set可以避免空间太大,重复数字。查询,插入均是logn的时间复杂度,也可以接受。

再说Setd的合并——启发式合并。其实就是每次合并把数字少的那组合并到数字多的那组以达到减少树的深度及合并次数而达到减少时间复杂度的效果。具体的图就不放了,自己看看其他oier的就好了

实现思路大致是,询问不等时,若两位数字所在的源头不同,则二个数字可以不同。分别在与他们相同的源头处限制它们互相相等。询问相等时,若两数字所在源头中维护不相等的set中没有对方的,则代表他们可以想等。此时,若两个数字出处一致则毫无大碍,否则就需要把数字少的源头的不等关系转嫁。即某人父亲和一个有孩子的离异女子结婚时,她的孩子也是他的孩子。即我们所说的启发式合并。

十分愉快的代码时间

(其实代码里我也打了很多注释)

 1 #include<iostream>
 2 #include<set>
 3 #include<cstdio>
 4 #include<map>
 5 using namespace std;
 6 //从全局变量->主程序->随着主程序看函数是个好习惯
 7 map<long long,long long>q;//离散化要用
 8 struct ziji{
 9     long long fa;//这个地方的父亲
10     set<long long> s;//要维护的不等关系
11     #define fa(i) mn[i].fa
12     #define s(i) mn[i].s
13 }mn[200001];
14 long long x,y,p,n,ff;//见主函数分析
15 inline long long father(long long i){
16     if(i==fa(i)) return i;
17     else return fa(i)=father(fa(i));
18     //简单的按秩合并
19 }
20 void message1(long long a,long long b){
21     long long root=father(a),root1=father(b);
22     // 找爸爸
23     if(s(root).find(root1)==s(root).end()) {//可以不等
24         putchar(‘Y‘),putchar(‘E‘),putchar(‘S‘),putchar(‘\n‘);
25         //这里一定要用putchar 或 puts ,用cout&&printf会超时
26         //反正我超时了
27         if(root!=root1){//非同源
28             if(s(root).size()>s(root1).size()) swap(root,root1);
29             //小的合并到大的里头去——启发式合并
30             fa(root)=root1;
31             set<long long>::iterator it=s(root).begin();
32             for(it;it!=s(root).end();it++)
33                 s(root1).insert(*it),s(*it).insert(root1),s(*it).erase(root);
34                 //暴力合并set
35         }
36     }
37     else putchar(‘N‘),putchar(‘O‘),putchar(‘\n‘);
38 }
39 void message2(long long a,long long b){//维护不等关系
40     int root=father(a),root1=father(b);
41     //找爸爸
42     if(root!=root1){//不相等
43         putchar(‘Y‘),putchar(‘E‘),putchar(‘S‘);putchar(‘\n‘);
44         s(root).insert(root1);s(root1).insert(root);
45         //二位父亲互不相等
46     }
47     else putchar(‘N‘),putchar(‘O‘),putchar(‘\n‘);
48 }
49 void chuli(int v){
50     //初始并查集时,由于仅仅是操作数是n
51     //最惨就是每次都是俩不同的,所以i<=v*2
52     for(register int i=1;i<=v*2;i++) fa(i)=i;
53 }
54 int main(){
55     scanf("%lld",&n);
56     chuli(n);//初始并查集
57     for(register int i=1;i<=n;i++){
58         scanf("%lld%lld%lld",&x,&y,&p);
59         //下面就是离散化处理了,我这里用了map
60         //据另一位大佬说,这里可以用sort+去重离线做
61         //但我更喜欢map,有本事打我啊
62         if(!q.count(x)){//如果这个数之前没有出现过
63             ff++;//一共出现的数总数++
64             q[x]=ff;x=ff;//以后这个数再调用就是ff,此时这个数离散成了ff
65         }else x=q[x];//出现了,就是当时被离散的值
66         if(!q.count(y)){//同理
67             ff++;
68             q[y]=ff;y=ff;
69         }else y=q[y];
70         if(p==1) message1(x,y);
71         if(p==0) message2(x,y);
72     }
73     return 0;//功德圆满,我好困啊
74 }

代码

国际惯例

如果有什么疑问,请在我的博客下留言,或者看题解刚开始我给的那个大佬的博客,他似乎也写了。我会尽我的努力为您提供答案。虽然可能不及时,希望可以谅解。thankyou for your attention

原文地址:https://www.cnblogs.com/fallen-down/p/10703816.html

时间: 2024-08-03 17:29:35

题解51nod1515——明辨是非的相关文章

[51nod1515]明辨是非

Description 给组操作,每组操作形式为. 当时,如果第变量和第个变量可以相等,则输出,并限制他们相等;否则输出,并忽略此次操作. 当时,如果第变量和第个变量可以不相等,则输出,并限制他们不相等;否则输出,并忽略此次操作. Input 输入一个数表示操作的次数.接下来行每行三个数. Output 对于行操作,分别输出行或者. Sample Input 3 1 2 1 1 3 1 2 3 0 Sample Output YES YES NO HINT . Solution 离散化所有的变量

51nod-1515 明辨是非——并查集

给n组操作,每组操作形式为x y p. 当p为1时,如果第x变量和第y个变量可以相等,则输出YES,并限制他们相等:否则输出NO,并忽略此次操作. 当p为0时,如果第x变量和第y个变量可以不相等,则输出YES,并限制他们不相等 :否则输出NO,并忽略此次操作. Input 输入一个数n表示操作的次数(n<=1*10^5) 接下来n行每行三个数x,y,p(x,y<=1*10^8,p=0 or 1) Output 对于n行操作,分别输出n行YES或者NO Input示例 3 1 2 1 1 3 1

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

8.8联考题解

今天的T1让我怀疑我是不是在做奥赛题--这考的是什么知识点啊这个,会不会用绝对值函数? Evensgn 的债务 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Evensgn 有一群好朋友,他们经常互相借钱.假如说有三个好朋友A,B,C.A 欠 B 20 元,B 欠 C 20 元,总债务规模为 20+20=40 元.Evensgn 是个追求简约的人,他觉得这样的债务太繁杂了.他认为,上面的债务可以完全等价为 A 欠C20 元,B 既不欠别人,别人也不欠他.这样总债务规模就压缩到了 

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

(leetcode题解)Pascal&#39;s Triangle

Pascal's Triangle  Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Return [ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1] ] 题意实现一个杨辉三角. 这道题只要注意了边界条件应该很好实现出来,C++实现如下 vector<vector<int>> generate(int

2017ZZUACM省赛选拔试题部分题解----谨以纪念我这卡线滚粗的美好经历

写在前面: 其实心里有些小小的不爽又有点小小的舒畅,为啥捏?不爽当然是因为没被选拔上啦,舒畅捏则是因为没被选拔上反而让自己警醒,学长也提点很多很多."沉下去,然后一战成名"学长如是对我说,我很开心.其实这完全算不算是题解,只是我个人的一些小想法而已.而且到现在还有一题不会...让自己长点记性吧. 题目 A :聪明的田鼠 Time Limit: 1 Sec Memory Limit: 128 MB Description 田鼠MIUMIU来到了一片农田,农田可以看成是一个M*N个方格的矩

LeetCode-001题解

此题目摘自LeetCode001 Given an array of integers, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.

leetcode题解: Next Permutation

最近还一直在刷leetcode,当然,更多时候只是将题解写在自己的电脑上,没有分享出来.偶尔想起来的时候,就写出来. public class Solution { public void nextPermutation(int[] nums) { if(nums==null||nums.length<=1) return; nextPermutationHelp( nums,0,nums.length-1); } public void nextPermutationHelp(int []nu