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;
}

浙公网安备 33010602011771号