题解 P6185 【[NOI Online 提高组]序列(民间数据)】

Damo

2020-03-09 10:06:08

Solution

# 思路 大家都太强了……看了半天都是图论大神。 我只好写一个蒟蒻的做法,小小的 __带权并查集__。 我是这么想哒,考虑 $i,j$ 和 $j,k$ 两种操作,能够给 $i,k$ 带来什么: - 如果 $i,j$ 是可以同时增加的,而 $j,k$ 也是可以同时减少的,那么 $i,j$ 同时增加 $x$ ,$j,k$ 同时减少 $x$ ,完美地做到了 $i,k$ 一个增加、一个减少。 - 如果 $i,j$ 是可以同时增加的,而 $j,k$ 是一加一减的,那么 $i,k$ 通过 $j$ 这个中介,就可以做到 $i$ 增加一、$k$ 也增加一。 - 如果都是一加一减型,那么可以认为存在一个 $i,k$ 的一加一减型。 所以说,对于这种三个点的关系,其中一个作为中介的时候,如果 $i,j$ 和 $j,k$ 的状态不同,就可以做到同时增加;否则得到一增一减的结果。 于是,__把同时增加设为$1$、一增一减设为$0$,连边之后,二者的关系为路径的异或和__。而且,这也是自洽的,因为自己到自己的长度是$0$,相当于增加了又减少,没有问题。 然后就有了带权并查集的思路,因为 __所有的操作可以等价于直接与并查集的根操作__,毕竟是异或和,$dis(a,rt)\oplus dis(b,rt)=dis(a,b)$ 嘛,所以 $a,b$ 本来就可以选择 $root$ 作为中介点。 加出来一个环怎么办?如果这会导致一个长度为$1$的环,那么在根节点上打一个标记,表示“__可以自行消化偶数权值__”。否则,没什么鸟用,因为每个节点本身就有一个隐藏的长度为零的自环。 最后,暴力检查每一个根节点是否达到了要求即可,因为根节点就已经没有人能够跟它操作了,除了自环消化偶数。 我没有打启发式合并,普通的路径压缩,最坏是 $\mathcal O(n\log n)$ 的。还是能过。 ~~却因为没有清空挂掉了。事实上,三道题都因为奇怪的原因挂掉了~~ # 代码 实现的时候,可以只存储 $b_i-a_i$ ,也就是 $a_i$ 需要的变化量。 ```cpp #include <cstdio> #include <iostream> #include <vector> using namespace std; inline long long readint(){ long long a = 0; char c = getchar(), f = 1; for(; c<'0'||c>'9'; c=getchar()) if(c == '-') f = -f; for(; '0'<=c&&c<='9'; c=getchar()) a = (a<<3)+(a<<1)+(c^48); return a*f; } inline void writeint(long long x){ if(x < 0) putchar('-'), x = -x; if(x > 9) writeint(x/10); putchar((x%10)^48); } # define MB template < typename T > MB void getMax(T &a,const T &b){ if(a < b) a = b; } MB void getMin(T &a,const T &b){ if(b < a) a = b; } const int MaxN = 100005; int fa[MaxN], val[MaxN]; inline int findSet(int a){ if(fa[a] == a) return a; int root = findSet(fa[a]); val[a] ^= val[fa[a]]; return fa[a] = root; } bool win[MaxN]; // 是否有长度为1的自环 void unionSet(int a,int b,int c){ int x = findSet(a), y = findSet(b); int dis = val[a]^c^val[b]; if(x == y) win[x] = win[x] or dis == 1; else{ fa[x] = y, val[x] = dis; win[y] = win[y] or win[x]; } } long long a[MaxN]; int n, m; int main(){ // freopen("sequence.in","r",stdin); // freopen("sequence.out","w",stdout); for(int T=readint(); T; --T){ n = readint(), m = readint(); for(int i=1; i<=n; ++i){ a[i] = readint(); fa[i] = i, val[i] = 0; win[i] = false; } for(int i=1; i<=n; ++i) a[i] = readint()-a[i]; for(int opt,x; m; --m){ opt = readint()%2, x = readint(); unionSet(x,readint(),opt); } for(int i=1,rt; i<=n; ++i){ rt = findSet(i); if(rt == i) continue; if(val[i] == 1) // a[i]-=a[i],a[rt]-=a[i] a[rt] -= a[i]; // 权值同时增加a[i] ... else a[rt] += a[i]; // ... 需求便减少了 } bool ok = true; for(int i=1,rt; i<=n and ok; ++i){ rt = findSet(i); if(rt != i) continue; if(win[rt]) a[rt] %= 2; if(a[rt] != 0) ok = false; } if(ok) puts("YES"); else puts("NO"); } return 0; } ``` 也许是错的——希望有大佬在看出错误之后 ~~心狠手辣地、尖酸刻薄地~~ 指出,受教了!