给n组操作,每组操作形式为x y p。
当p为1时,如果第x变量和第y个变量可以相等,则输出YES,并限制他们相等;否则输出NO,并忽略此次操作。
当p为0时,如果第x变量和第y个变量可以不相等,则输出YES,并限制他们不相等 ;否则输出NO,并忽略此次操作。
输入一个数n表示操作的次数(n<=1*10^5) 接下来n行每行三个数x,y,p(x,y<=1*10^8,p=0 or 1)
对于n行操作,分别输出n行YES或者NO
3 1 2 1 1 3 1 2 3 0
YES YES NO
题解
思路很好的并查集的题。
以前做过一道程序自动分析的题,也是并查集,但那题对不相等的数没有记录,所以就是离散+排序+并查集就轻松秒掉。而这题对不相等的点也要进行记录,而并查集只能对于一个操作进行操作,所以还要一个数组记录和它不相等的集合的父亲,用set进行维护。
下面是具体操作。
把相等的变量存到一个并查集里,不相等的变量存到若干个集合中,s[i]集合存着所有与i不相等的变量所属并查集的根。每一次操作先获得x, y的所属并查集的根u, v.
当p == 0时,如果x和y在一个并查集,也就是u == v,那么就NO,否则互相插入对方的集合。
当p == 1时,在u的集合里查找是不是有v,用内置的count函数,或者判断find的返回值是否不等于s[u].end().
如果有,肯定是NO,否则要进行集合合并,因为集合里存的都是并查集的根,现在我要执行f[u] = v,就要把与u不相等的变量都加到v的集合里,同时把与u不相等的变量所在的集合都插入v和删除u(怎么说着这么别扭,自行看代码)(这块我竟然和zrt想的一样!)
为了降低开销,肯定是要把小的集合并到大的集合里,所以第一步进行了一次size()比较(不比较会MLE)
因为x, y <= 10^8,所以不能开个一亿的数组存储,观察到操作次数最多只会有100000次,也就是最多有200000个变量,那么开个200005的数组足够,问题就是如何将一个大数对应成一个200000以内的数呢?有两种方法:
第一种是先读入所有变量,sort排序后,unique去重,要求离线。
第二种是用map,详见代码,可支持在线。
这里采用可支持在线的第二种方法。
最后就是输出"YES"和"NO"的时候不要用printf,会超时,用puts(这是一些经验,要积累)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<set> #include<map> using namespace std; const int maxn=200010; int fa[maxn]; set<int>s[maxn]; map<int ,int >m; int find(int x){ if(fa[x]==x) return x; return fa[x]=find(fa[x]); } int main(){ int n,x,y,p,cnt=0; scanf("%d",&n); for(int i=1;i<=n*2;i++) fa[i]=i; for(int i=1;i<=n;i++){ scanf("%d%d%d",&x,&y,&p); if(m[x]) x=m[x]; else x=m[x]=++cnt; if(m[y]) y=m[y]; else y=m[y]=++cnt; int u=find(x),v=find(y); if(p==1){ if(s[u].count(v)) puts("NO"); else if(u!=v){ if(s[u].size()>s[v].size()) swap(u,v); fa[u]=v; for(set<int>::iterator it=s[u].begin();it!=s[u].end();++it){ s[*it].erase(u),s[*it].insert(v),s[v].insert(*it); } puts("YES"); } else puts("YES"); } else{ if(u==v) puts("NO"); else { s[u].insert(v); s[v].insert(u); puts("YES"); } } } return 0; }