[关闭]
@y20070316 2017-04-10T14:00:07.000000Z 字数 3752 阅读 916

XSY 2250 - 生日礼物 & HNOI 2013 - 游走 & BZOJ 3207 - 博物馆

专题练习 题目 高斯消元 概率论



总结: 高斯消元

1. 高斯消元的3个问题
① 有没有解: 考虑不含系数的一行, 若该行的结果非0, 那么无解.
② 有多少个解:
③ 求解: 回溯的时候考虑搜索, 初次的消元不要搜索.
2. 正确姿势

  1. int Free(void) {
  2. int cnt=0;
  3. F(i,1,ind) {
  4. if (!a[i][i]) F(j,i+1,ind) if (a[j][i]) { swap(a[i],a[j]); break; }
  5. if (!a[i][i]) { cnt++; continue; }
  6. F(j,i+1,ind) if (a[j][i]) a[j]=a[j]^a[i];
  7. }
  8. return cnt;
  9. }

不变的地方: 个未知数放在第 行.
变化的地方:
① 消不消掉上面的
② 消的方法(逆元, 直接除, 辗转)
③ 交换的方法(浮点数取最小)
④ 对于异或方程, 可以使用 bitset 进行优化.

3. 相关方法
常用于 行列式求值 , 矩阵求逆 , 线性基 , 解方程 .
对于棋盘的问题, 通常只要设部分的未知数, 然后递推求解其他的未知数.

XSY 2250 - 生日礼物

题意

   的开关, 最初全部都没有亮. 每按一个开关, 能将 的所有位置的状态取反.
  求有多少种方法, 能将所有的灯亮.
  

分析

  暴力的想法: 设 个未知数, 高斯消元.
  高斯消元可以求出自由元的个数 , 然后答案为 .
  但是 显然会 TLE .

  注意到这是一个棋盘, 我们假如能知道第一行, 第二行, 第一列的所有状态.
  对于另外的一个状态 , 我们可以通过 推出 . 此时 一定有解, 相当于把 变成了 .
  那么最终考虑后两行, 以及后一列的所有状态即可. 这样我们需要设的状态数就是 的了.
  Update: 更本质的理解是设 个未知数, 进行了 次代入, 最后剩下的就是最后的两行, 以及最后的一列.

  发现依然过不了, 由于它是异或方程, 只有 0 和 1 , 所以我们考虑使用 bitset 进行优化.

  经打表找规律发现, 该问题一定有解. 所以不需要存储异或和, 只需要系数矩阵即可.

实现

  1. #include <cstdio>
  2. #include <algorithm>
  3. #include <utility>
  4. #include <bitset>
  5. using namespace std;
  6. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  7. const int N=605;
  8. const int L=1850;
  9. const int dx[9]={ -2 , -2 , -1 , -1 , 0 , 1 , 1 , 2 , 2 };
  10. const int dy[9]={ -1 , 1 , -2 , 2 , 0 , -2 , 2 , -1 , 1 };
  11. int n,m;
  12. bitset<L> s[N][N]; int tot;
  13. bitset<L> a[L]; int ind;
  14. inline int check(int x,int y) { return 1<=x&&x<=n&&1<=y&&y<=m; }
  15. int Free(void) {
  16. int cnt=0;
  17. F(i,1,ind) {
  18. if (!a[i][i]) F(j,i+1,ind) if (a[j][i]) { swap(a[i],a[j]); break; }
  19. if (!a[i][i]) { cnt++; continue; }
  20. F(j,i+1,ind) if (a[j][i]) a[j]=a[j]^a[i];
  21. }
  22. return cnt;
  23. }
  24. int main(void) {
  25. #ifndef ONLINE_JUDGE
  26. freopen("present.in","r",stdin);
  27. freopen("present.out","w",stdout);
  28. #endif
  29. scanf("%d%d",&n,&m); if (n==1) return printf("1\n"),0;
  30. F(i,1,2) F(j,1,m) s[i][j][++tot]=1;
  31. F(i,3,n) s[i][1][++tot]=1;
  32. F(i,3,n) F(j,2,m) {
  33. int x=i-2,y=j-1;
  34. F(k,0,8) {
  35. int _x=x+dx[k],_y=y+dy[k];
  36. if (check(_x,_y)&& (_x!=i||_y!=j) )
  37. s[i][j]=s[i][j]^s[_x][_y];
  38. }
  39. }
  40. pair<int,int> go[L];
  41. F(i,n-1,n) F(j,1,m) go[++ind]=make_pair(i,j);
  42. F(i,1,n-2) go[++ind]=make_pair(i,m);
  43. F(i,1,ind) {
  44. int x=go[i].first,y=go[i].second;
  45. F(k,0,8) {
  46. int _x=x+dx[k],_y=y+dy[k];
  47. if (check(_x,_y))
  48. a[i]=a[i]^s[_x][_y];
  49. }
  50. }
  51. int t=Free();
  52. int mul=1; F(i,1,t) mul=(mul+mul)%123456789; printf("%d\n",mul);
  53. return 0;
  54. }

HNOI 2013 - 游走

题意

  无向简单图, 个点, 条边。
  从点 出发,等概率选边走到点
  给边进行编号,使得从 走到 的路径上的边的编号之和的期望值最小。
  

分析

  https://blog.sengxian.com/solutions/bzoj-3143

实现

  1. #include <cstdio>
  2. #include <cctype>
  3. #include <cmath>
  4. #include <algorithm>
  5. #include <functional>
  6. using namespace std;
  7. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  8. const int N=505;
  9. int n,m; int d[N],tot,hd[N]; struct Edge { int v,nx; inline Edge(int _v=0,int _nx=0):v(_v),nx(_nx){} }mp[500005];
  10. double f[N][N]; double ans[N];
  11. double cnt[250005],sum;
  12. inline int rd(void) {
  13. int x=0,f=1; char c=getchar();
  14. for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
  15. for (;isdigit(c);c=getchar()) x=x*10+c-'0';
  16. return x*f;
  17. }
  18. inline void Ins(int u,int v) {
  19. mp[++tot]=Edge(v,hd[u]); hd[u]=tot; d[u]++;
  20. mp[++tot]=Edge(u,hd[v]); hd[v]=tot; d[v]++;
  21. }
  22. int main(void) {
  23. #ifndef ONLINE_JUDGE
  24. freopen("sdchr.in","r",stdin);
  25. #endif
  26. tot=1; n=rd(),m=rd(); F(i,1,m) { int u=rd(); Ins(u,rd()); }
  27. F(p,1,n) {
  28. f[p][p]=1; f[p][n+1]=(p==1);
  29. for (int k=hd[p];k>0;k=mp[k].nx) if (mp[k].v!=n)
  30. f[p][mp[k].v]=-1.0/d[mp[k].v];
  31. }
  32. F(i,1,n) {
  33. if (f[i][i]==0) {
  34. int id=i+1; F(j,i+2,n) if (fabs(f[j][i])>fabs(f[id][i])) id=j;
  35. F(k,1,n+1) swap(f[i][k],f[id][k]);
  36. }
  37. F(j,1,n) if (i!=j&&f[j][i]!=0) {
  38. double t=f[j][i]/f[i][i];
  39. F(k,i,n+1) f[j][k]=f[j][k]-t*f[i][k];
  40. }
  41. }
  42. F(i,1,n) ans[i]=f[i][n+1]/f[i][i];
  43. F(p,1,n-1) for (int k=hd[p];k>0;k=mp[k].nx)
  44. cnt[k>>1]+=ans[p]/d[p];
  45. sort(cnt+1,cnt+m+1,greater<double>());
  46. F(i,1,m) sum+=i*cnt[i]; printf("%0.3lf\n",sum);
  47. return 0;
  48. }

BZOJ 3270 - 博物馆

题意

  给定一张 个点, 条边的无向图. 给定 的初始位置. 每次 会进行随机游走, 相遇的时候停下. 问 在每个点相遇的概率.
  

分析

  由于 相遇就停止, 所以 每个点相遇的期望次数 = 1次 * 每个点相遇的概率 . 我们考虑求出 : 在第 个点相遇的期望次数.
  那么, 对于起点, , 对于其他点, .
  高斯消元即可.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注