連結成分毎に考えてよい. 連結なグラフで考えるとき,まずはtreeで考えるとよい. Spannig treeを用いて,treeの場合に帰着できるケースも多いし, まずtreeで解けないと,一般の場合は話にならない.
また,操作の性質として,操作によって値の総和が不変であることが分かる.
Treeで考えると,葉から値を決めていけばよい. もしtreeのvertex が2個以上なら,最後に2箇所だけ値が定まってない状態になる. このときに,+x, -x の形になっているのが,条件を満たす必要十分条件. つまり,vertex に書いてある値の和が0であることが必要十分. これは,vertex の個数が1個の場合でも成立.
この考えをspanning tree に適用すれば, treeでない連結成分でも同様.
実装するときは,vertex に を書き込めばよい. DFS, Uinon-find どちらで連結成分を判定してもAC.
使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"
int main() {
ll n, m;
cin >> n >> m;
vll v(n);
cinv(va);
rep(i,n){
}
UnionFind<ll> uf(n);
vvll to(n);
rep(i,m){
ll c,d ; cin >> c >> d;
--c,--d;
uf.merge(c,d);
to[c].push_back(d);
to[d].push_back(c);
}
{// AC : union-find
vll x(n);
rep(i,n){
x[uf[i]] += v[i];
}
bool any = true;
rep(i,n){
if(x[i]!=0) any = false;
}
yesno(any);
}
{ // AC : dfs
// vll vis(n);
// ll s = 0;
// auto dfs = [&](auto f, ll cu, ll pa) -> void {
// if(vis[cu]) return ;
// vis[cu] = true;
// s += v[cu];
// fore(ne, to[cu]) if(ne != pa){
// f(f, ne, cu);
// }
// };
// rep(i,n) if(!vis[i]){
// s = 0;
// dfs(dfs, i, -1);
// if(s != 0) {
// cout << "No" << endl;
// return 0;
// }
// }
// cout << "Yes" << endl;
}
#endif
return 0;
}