浅论 T2 带权并查集做法
前言
不知道对不对,但是我觉得很对。
约定:T=1, U=0, F=-1=Z
正文
首先我们手玩一下样例。对于每次赋值u:=v,我们画一条从 u 指向 v 的箭头,并在箭头上标 + 或者 -,表示 u 依赖于 v 。然而我们发现,有些似乎图画出来是这样的:
2 1 Z
1 3 1
3 2 1
(我们约定 Z=-1 表示取反,1 表示取同,并且将操作后置)
欸怎么还有环呢?显然依赖不能成环,或者全得置U。如果按照这张图,则仅有全 U 的方案满足题意。
但是不对劲,我们发现时间上有问题。当 3 被 1 赋值时,2 已经被 3 赋值过了,不会再赋一遍,因而这种依赖是不正确的。
很自然地我们想到,像分层图那样给节点标上时间。方便起见,我们令 i 表示 i' 则表示修改过的 i 位置上最新的数。则原图应该是:
2' 1 Z
1' 3 1
3' 2' 1
这样就对了。
我们再观察图片,会发现有些结构是不必要的。我们压缩长度超过 1 的路径。
题目要求最终变量与初值相等,很自然地我们要合并这两棵树,使得它们的相对关系确定下来。
我们发现这种操作很像并查集:路径压缩和合并都有了。因为边带权,所以我们叫他带权并查集。
带权并查集边的意义并非只能是“类型相同”,还可以是 Z 的“类型取异相等”等。两点间,有连边(无论方向)仅仅表示它们的相对关系确定。
我们于是连一条边3 1 1 使得 3 和 3’ 的值相等。
刚好 1 1' 也同类型。但是若不等呢?如
2' 1 Z
1' 3 Z
3' 1 1
时,又该怎么办?
我们先对齐 3 和 3',然后发现……
ooops! 1 和它的末值不相等。
该如何避免它呢?
能不能换顺序,即先对齐 1 呢?
不行。因为 “3==3‘” 已经蕴含了 "1=!1'",我们才得以导出这个结构。本质上,是 "3==3’"和“1==1‘”的矛盾,怎样交换顺序都不行。
我们发现,只能有一种解决方法:令 1==1'==0 。因为 0*Z=0。题目保证了一定有解,所以不会出现 1 已经被强制赋成 TF 的情况。
我们的思路已经清晰:既然冲突数量与顺序无关,我们可以按任意顺序合并集合。若产生冲突,赋值为 0 即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+100;
const int T=3e5+11, F=3e5+12, U=3e5+13;
int fa[maxn],w[maxn],lst[maxn],psz;
int Find(int u) {
if(fa[u]) {
int f=fa[u];
fa[u]=Find(fa[u]);
w[u]=w[u]*w[f];
return fa[u];
}else return u;
}
void Merge(int u,int v,int c) { // u:=v , u->v
int fu=Find(u),fv=Find(v);
if(fu==fv) {
if(w[u]*w[v]!=c&&!(w[u]==0&&w[v]==0)) {
if(fu>1e5+10) assert(0);
w[fu]=0;
// cout<<'!';
}
return;
}
fa[fu]=fv;
w[fu]=w[u]*w[v]*c;
return;
// Find(fv);
}
inline int acc(int u) {
return lst[u]=++psz;
}
void init(int n) {
memset(fa,0,sizeof fa); //???
fill(w,w+10+n*3,1);
for(int i=1;i<=n;i++) lst[i]=i;
}
void work() {
int n,m;
cin>>n>>m;
init(n);
psz=n;
while(m--) {
char op;
int u,v;
cin>>op;
if(op=='T') {
cin>>u;
Merge(acc(u),T,1);
}
else if(op=='U') {
cin>>u;
Merge(acc(u),U,1);
}
else if(op=='F') {
cin>>u;
Merge(acc(u),F,1);
}
else if(op=='+') {
cin>>u>>v;
Merge(acc(u),lst[v],1);
}
else if(op=='-') {
cin>>u>>v;
Merge(acc(u),lst[v],-1);
}
else assert(false);
}
for(int i=1;i<=n;i++) { //n????ê?£ow[lst[i]]==w[i]
// if(w[lst[i]]==w[i]) continue;
// if(Find(i)==Find(lst[i])) {
// w[Find(i)]=0;
// }
Merge(i,lst[i],1);
}
int cnt=0;
for(int i=1;i<=n;i++) {
Find(i);
}
for(int i=1;i<=n;i++) {
// cout<<i<<':'<<char(w[i]==-1?'F':w[i]?'T':'U')<<'\n';
if(w[i]==0) cnt++;
}
// cout<<cnt<<'\n';
}
int main() {
// freopen("tribool.in","r",stdin);
// freopen("tribool.out","w",stdout);
int c,Tc;
cin>>c>>Tc;
w[U]=0,w[F]=-1,w[T]=1;
while(Tc--) {
work();
}
}
但是你谷 40 pts.