P3211 [HNOI2011]XOR和路径

很明显期望 dp。

开始时想到设 \(f_i\)\(i\)\(n\) 的期望值。

但由于这题是异或,所以要拆分成 \(2\) 进制,分成每一位的子任务解决。

那么改变定义,设 \(f_i\) 为点 \(i\)\(n\)\(x\) 位为 \(1\) 的期望值,\(r_i\) 代表 \(i\) 号点的出度。对于每一条边 \((i,j)\) 那么:

\[f_i=\frac{1}{r_i}(\sum_{(i,j)=0}f_j+\sum_{(i,j)=1}(1-f_j)) \]

\[r_if_i-\sum_{(i,j)=0}f_v+\sum_{(i,j)=1}f_v=\sum_{(i,j)=1}1 \]

那么我们可以把 \(f_i\) 看成未知数,先建方程,再解方程即可。

但是 \(n\) 个未知数,\(n-1\) 个方程,所以还有一个初始方程 \(f_n=0\)

很好理解,因为到了点 \(n\) 以后无法再移动,所以点 \(n\)\(n\) 期望值赋为 \(0\)

答案就是起始点 \(1\) 的期望值乘上对应的位数,即 \(\sum f_1\times 2^{x-1}\)

复杂度:\(O(n^3)\)

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define int long long
using namespace std;
const int N=105;
int n,m,cnt,r[N];
double ans[N],t[N][N];
struct node
{
	int to,data;
};
vector<node>a[N];
inline int tabs(int x)
{
	return x>0?x:-x;
}
void build(int x)
{
	memset(t,0,sizeof(t));
	t[n][n]=1;
	for(int i=1;i<n;i++)
	{
		t[i][i]=r[i];
		int len=a[i].size();
		for(int j=0;j<len;j++)
		{
			if(a[i][j].data&x)t[i][a[i][j].to]++,t[i][n+1]++;
			else t[i][a[i][j].to]--;
		}
	}
}
void xiao()
{
	ans[1]=0;
	for(int i=1;i<=n;i++)
	{
		int ma=i;
		for(int j=i+1;j<=n;j++)if(tabs(t[j][i])>tabs(t[ma][i]))ma=i;
		for(int j=1;j<=n+1;j++)swap(t[i][j],t[ma][j]);
		if(!t[i][i])return;
		for(int j=1;j<=n;j++)
		{
			if(j==i)continue;
			double num=t[j][i]/t[i][i];
			for(int k=1;k<=n+1;k++)t[j][k]-=t[i][k]*num;
		}
	}
	for(int i=1;i<=n;i++)ans[i]=t[i][n+1]/t[i][i];
}
signed main()
{
	//freopen("xor.in","r",stdin);
	//freopen("xor.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%lld%lld%lld",&u,&v,&w);
		a[u].push_back((node){v,w}),r[u]++;
		if(u!=v)a[v].push_back((node){u,w}),r[v]++;
	}
	double res=0;
	for(int i=1;i<=1e9;i<<=1)
	{
		build(i);
		xiao();
		res+=i*ans[1];
	}
	printf("%.3f",res);
	return 0;
}
posted @ 2023-02-24 13:34  Gmt丶Fu9ture  阅读(30)  评论(0)    收藏  举报