题目描述:
给$n$组操作,每组操作形式为 x y p。
当$p$为 1 时,如果第$x$变量和第$y$个变量可以相等,则输出YES,并限制他们
相等;否则输出 NO,并忽略此次操作。
当$p$为 0 时,如果第$x$变量和第$y$个变量可以相等,则输出 YES,并限制他
们相等;否则输出 NO,并忽略此次操作。
思路:
看到维护相等关系,自然的想到并查集。
但是本题要维护相等和不等关系,普通的并查集无法满足。
之后怎么办呢?在考场的我就懵逼了。后来发现用set暴力维护每个并查集内不能和谁相等就完了。好弱智啊!我果然还是太菜了。
在合并相等关系时可以找到元素个数较少的一个set,将他直接插进另一个set里。这种暴力被称为启发式合并。奇技淫巧
合并不等关系时直接加入set里就好了。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<set> #include<map> using namespace std; set<int>s[2000005]; set<int>::iterator it; map<int,int>a; int f[2000005]; int find(int x){return f[x]==x?x:f[x]=find(f[x]);} int main() { // freopen("TrueFalse.in","r",stdin); // freopen("TrueFalse.out","w",stdout); int n; scanf("%d",&n); int i,cnt=0; for(i=1;i<=n*2;i++) f[i]=i; for(i=1;i<=n;i++) { int x,y; scanf("%d%d",&x,&y); a[x]?(x=a[x]):(x=a[x]=++cnt); a[y]?(y=a[y]):(y=a[y]=++cnt); int opt; scanf("%d",&opt); int u=find(x); int v=find(y); if(opt==1) { if(s[u].count(v)) puts("NO"); else if(u!=v) { if(s[u].size()>s[v].size()) swap(u,v); f[u]=v; for(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"); } } } }