これは典型かな…。
https://yukicoder.me/problems/no/2377
問題
N頂点の木を成す無向グラフが与えられる。
また各点には非負整数値が設定されている。
(N-1)の辺のうち、いくつかを消す方法は2^(N-1)通りある。
それぞれのグラフにおける、以下の総和を求めよ。
- 連結成分毎に、頂点の設定値のXORを取る。
- その値を、連結成分間でANDを取る。
解法
XORもANDも各整数値の2進数表記における特定の桁にしか影響しない。
よって、桁ごとに考えよう。
辺の消し方を定めたとき、グラフにおける上記計算値のある桁が1になるのは、各連結成分内でその桁が1となる頂点数が奇数の時だけである。
よって、DPで連結したSubTree内の1が立った頂点数の偶奇を状態として持ち、数え上げることができる。
int N; vector<int> E[202020]; int A[202020]; int C[202020]; ll dp[202020][2]; const ll mo=998244353; void dfs(int cur,int pre) { ll from[2]={0,0}; from[C[cur]]=1; FORR(e,E[cur]) if(e!=pre) { dfs(e,cur); ll to[2]={}; //つなげる (to[0]+=from[0]*dp[e][0]+from[1]*dp[e][1])%=mo; (to[1]+=from[1]*dp[e][0]+from[0]*dp[e][1])%=mo; //繋げない (to[0]+=from[0]*dp[e][1])%=mo; (to[1]+=from[1]*dp[e][1])%=mo; swap(from,to); } dp[cur][0]=from[0]; dp[cur][1]=from[1]; } void solve() { int i,j,k,l,r,x,y; string s; cin>>N; FOR(i,N-1) { cin>>x>>y; E[x-1].push_back(y-1); E[y-1].push_back(x-1); } FOR(i,N) cin>>A[i]; ll ret=0; FOR(i,30) { FOR(j,N) C[j]=(A[j]>>i)&1; dfs(0,0); (ret+=dp[0][1]<<i)%=mo; } cout<<ret<<endl; }
まとめ
すんなり行ってよかった。