题目描述
给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数。试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。
直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 N 号节点为止,便得到一条从 1 号节点到 N 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR 和”的期望值。
输入输出格式
输入格式:
从文件input.txt中读入数据,输入文件的第一行是用空格隔开的两个正整数N和M,分别表示该图的节点数和边数。紧接着的M行,每行是用空格隔开的三个非负整数u,v和w(1≤u,v≤N,0≤w≤109),表示该图的一条边(u,v),其权值为w。输入的数据保证图连通,30%的数据满足N≤30,100%的数据满足2≤N≤100,M≤10000,但是图中可能有重边或自环。
输出格式:
输出文件 output.txt 仅包含一个实数,表示上述算法得到的路径的“XOR 和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算)
Sol:
好题……
我们知道Gauss消元的经典应用就是无向图DP
数据范围N<=100 ok高斯消元
然后异或没有办法直接加减之类的就选择按位做
对于每一位做出期望然后×(1<<k)加起来就行
于是后面对于每一位做的时侯图就变成只有0/1边
初始递推式子是f[i]表示i->n的期望值
所以根据异或的定义
(∑u->v∈E) f[v] = f[u] / du[u] (cst[i] == 0)
(∑u->v∈E) f[v] = (1-f[u]) / du[u] (cst[i]==1)
高斯消元的时候就转化一下 未知数还是老套路dp[i]为第i个未知数就OK
每次的答案就是a[1][n+1](注意不是ans[1])
注意无向图自环特判 不然会加两次(样例还是比较良心的)
Code:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<queue> 6 #include<vector> 7 #define ms(a,b) memset(a,b,sizeof a) 8 #define rep(i,a,n) for(int i = a;i <= n;i++) 9 #define per(i,n,a) for(int i = n;i >= a;i--) 10 #define inf 2147483647 11 using namespace std; 12 typedef long long ll; 13 ll read() { 14 ll as = 0,fu = 1; 15 char c = getchar(); 16 while(c < '0' || c > '9') { 17 if(c == '-') fu = -1; 18 c = getchar(); 19 } 20 while(c >= '0' && c <= '9') { 21 as = as * 10 + c - '0'; 22 c = getchar(); 23 } 24 return as * fu; 25 } 26 const int N = 105; 27 const int M = 20005; 28 typedef double D; 29 #define eps 1e-7 30 //head 31 int n,m; 32 int head[N],nxt[M],cst[M],mo[M],cnt; 33 int du[N]; 34 void _add(int x,int y,int w) { 35 mo[++cnt] = y; 36 cst[cnt] = w; 37 nxt[cnt] = head[x]; 38 head[x] = cnt; 39 } 40 void add(int x,int y,int w) { 41 if(x^y) _add(x,y,w), _add(y,x,w),du[x]++,du[y]++; 42 else _add(x,x,w),du[x]++; 43 } 44 45 D a[N][N],ans[N]; 46 D Ans; 47 void Gauss() { 48 rep(i,1,n) { 49 int maxx = i; 50 rep(j,i+1,n) if(fabs(a[j][i]) > fabs(a[maxx][i])) maxx = j; 51 if(maxx ^ i) swap(a[maxx],a[i]); 52 D div = a[i][i]; 53 rep(j,i,n+1) a[i][j] /= div; 54 rep(j,1,n) { 55 if(j == i || !a[j][i]) continue; 56 div = a[j][i]; 57 rep(k,i,n+1) a[j][k] -= div * a[i][k]; 58 } 59 } 60 } 61 //这里就是上面求f[]的式子移项一下 62 void init(int k) { 63 ms(a,0); 64 rep(x,1,n-1) { 65 a[x][x] = 1.0; 66 for(int i = head[x]; i; i = nxt[i]) { 67 int sn = mo[i]; 68 if(cst[i] & (1<<k)) { 69 a[x][sn] += 1.0/du[x]; 70 a[x][n+1] += 1.0/du[x]; 71 } else a[x][sn] -= 1.0/du[x]; 72 } 73 } 74 a[n][n] = 1.0; 75 return; 76 } 77 78 int main() { 79 n = read(); 80 m = read(); 81 rep(i,1,m) { 82 int x = read(),y = read(),w = read(); 83 add(x,y,w); 84 } 85 rep(i,0,30) { 86 init(i); 87 Gauss(); 88 Ans += a[1][n+1] * D(1<<i);//所有点到n的期望和 89 } 90 printf("%.3lf ",Ans); 91 return 0; 92 }
稳妥