suxxsfe

一言(ヒトコト)

P3211 [HNOI2011]XOR和路径

https://www.luogu.com.cn/problem/P3211
用到在 dp 的转移中出现环时,把按顺序转移改为解方程的思路

整体计算比较困难,考虑把每一位拆开来算
对于当前位,设 \(f_u\) 表示从 \(u\)\(n\),当前位为 \(1\) 的概率;设 \(deg_u\)\(u\) 的度数,并令 \(w(u,v)\) 表示边权上当前这一位是 \(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) \]

但发现这是一个一般的图,转移过程中会出现环,那么就可以把他化成方程的形式,用解方程的方法来处理

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

这样对于每一个 \(1\le u \le n-1\) 都可以写出这样一个方程,对于第 \(n\) 个方程,由于 \(f_n=0\),所以直接令 \(b_n=0,a_{nn}=1\),即可
最后高斯消元就行了

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define reg register
#define LL_INF (long long)(0x3f3f3f3f3f3f3f3f)
#define INT_INF (int)(0x3f3f3f3f)
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
#define N 106
#define M 20006
struct Graph{
	int fir[N],nex[M],to[M],w[M],tot;
	inline void add(reg int u,int v,int z){
		to[++tot]=v;w[tot]=z;
		nex[tot]=fir[u];fir[u]=tot;
	}
}G;
int n,m;
int deg[N],a[N][N];
double b[N],z[N],f[N];
double u[N][N],l[N][N];
inline void lu_div(){
	for(reg int i=1;i<=n;i++){
		for(reg int j=i;j<=n;j++){
			u[i][j]=a[i][j];
			for(reg int k=1;k<i;k++) u[i][j]-=l[i][k]*u[k][j];
			l[j][i]=a[j][i];
			for(reg int k=1;k<i;k++) l[j][i]-=l[j][k]*u[k][i];
			l[j][i]/=u[i][i];
		}
		l[i][i]=1;
	}
}
inline void solve_z(){
	for(reg int i=1;i<=n;i++){
		z[i]=b[i];
		for(reg int j=1;j<i;j++) z[i]-=z[j]*l[i][j];
	}
}
inline void solve_f(){
	for(reg int i=n;i;i--){
		f[i]=z[i];
		for(reg int j=n;j>i;j--) f[i]-=f[j]*u[i][j];
		f[i]/=u[i][i];
	}
}
inline void solve(){lu_div();solve_z();solve_f();}
inline void build(int base){
	for(reg int u=1;u<n;u++){
		for(reg int j=1;j<=n;j++) a[u][j]=0;
		b[u]=0;
		a[u][u]=deg[u];
		for(reg int v,i=G.fir[u];i;i=G.nex[i]){
			v=G.to[i];
			if(G.w[i]&base) a[u][v]++,b[u]++;
			else a[u][v]--;
		}
	}
	b[n]=0;a[n][n]=1;
}
int main(){
//		std::freopen("out.txt","w",stdout);
	n=read();m=read();
	for(reg int u,v,w,i=1;i<=m;i++){
		u=read();v=read();w=read();
		G.add(u,v,w);deg[u]++;
		if(u^v) G.add(v,u,w),deg[v]++;
	}
	double ans=0;
	for(reg int i=0;i<=30;i++){
		build(1<<i);solve();
		ans+=f[1]*(1<<i);
//			for(reg int i=1;i<=n;i++){
//				for(reg int j=1;j<=n;j++) printf("%d ",a[i][j]);
//				puts("");
//			}
	}
	printf("%.3lf\n",ans);
	return 0;
}
posted @ 2021-03-11 21:25  suxxsfe  阅读(72)  评论(0编辑  收藏  举报