题解——P3211 [HNOI2011]XOR和路径


在此之前,建议先完成本题,解决方式是类似的。

(1)转移方程

我们设置 \(f_u\) 表示节点 \(u\)\(n\) 路径异或和的期望,发现这是由与 \(u\) 相连的节点转移而来的,于是就有:

\[f_u=\frac{1}{deg_u}\sum_{(u,v)\in E}(f_v \operatorname{xor} w) \]

然而我们面临两个问题。

首先,这个方程并非在 \(\operatorname{DAG}\) 上,不能靠拓扑排序等方式直接转移。其次,若使用上面例题“游走”的高斯消元,这个 \(\operatorname{xor}\) 运算也是不可做的。

(2)求解

然而你发现,期望是线性的,因此每一位上的贡献之和(按位进制转换后)就是我们的答案,因此考虑把每个权值拆开。发现每一位贡献只可能是 \(0/1\),然后仿照上式就得到:

\[f_u=\frac{1}{deg_u}\left(\sum_{w(u,v)=0} f_v+\sum_{w(u,v)=1} (1-f_v)\right) \]

然后把式子再拆一拆,就能得到:

\[f_u\times deg_u-\sum_{w(u,v)=0} f_v+\sum_{w(u,v)=1}f_v=\sum_{w(u,v)=0}1 \]

显然套到高斯消元里就能求解了。

inline void G(int n){
	for(int i=1;i<=n;i++){
		int pos=i;
		for(int j=i+1;j<=n;j++){
			if(fabs(a[j][i])-fabs(a[pos][i])>eps)
				pos=j;
		}
		if(pos!=i) swap(a[i],a[pos]);
		db q=a[i][i];
		for(int j=i;j<=n+1;j++){
			a[i][j]/=q;
		}
		for(int j=1;j<=n;j++){
			if(i!=j){
				q=a[j][i];
				for(int k=i;k<=n+1;k++){
					a[j][k]-=q*a[i][k];
				}
			}
		}
	}
}
db ans;
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		add_edge(u,v,w); deg[u]++;
		if(u!=v){
			add_edge(v,u,w); deg[v]++;
		}
	}
	for(int k=30;k>=0;k--){
		memset(a,0,sizeof(a));
		for(int u=1;u>k)&1;
				if(w){
					a[u][v]-=1.0; a[u][n+1]-=1.0;
				}
				else{
					a[u][v]+=1.0;
				}
			}
		}
		a[n][n]=1.0;
		G(n);
		ans+=(1<