kmjp's blog

競技プログラミング参加記です

yukicoder : No.2377 SUM AND XOR on Tree

これは典型かな…。
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;
	
}

まとめ

すんなり行ってよかった。