高斯消元小结
1.bzoj 1013 球形空间产生器sphere
最裸的高斯消元,直接套版
设球心坐标为(x1,x2...xn)
那么对于任意两点都可以建立一个方程: (x1-a1)2+(x2-a2)2...+(xn-an)2=(x1-b1)2+(x2-b2)2+...+(xn-bn)2
移项后可得: 2(a1-b1)x1+2(a2-b2)x2+ ... +2(an-bn)xn=a12-b12+...+an2-bn2
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <cmath> 5 using namespace std; 6 const double eps=1e-10; 7 double x,b[30],a[30][30]; 8 int n; 9 10 void gauss() { 11 for (int i=1;i<=n;i++) { 12 int p=i; 13 while (p<=n&&fabs(a[p][i])<eps) p++; 14 if (fabs(a[p][i])>eps) { 15 if (i^p) for (int j=i+1;j<=n+1;j++) swap(a[p][j],a[i][j]); 16 double t=a[i][i]; 17 for (int j=i+1;j<=n+1;j++) a[i][j]/=t; 18 for (int j=1;j<=n+1;j++) if (i^j) { 19 t=a[j][i]; 20 for (int k=1;k<=n+1;k++) 21 a[j][k]-=t*a[i][k]; 22 } 23 } 24 } 25 } 26 27 int main() 28 { 29 scanf("%d",&n); 30 for (int i=1;i<=n;i++) scanf("%lf",&b[i]); 31 for (int i=1;i<=n;i++) { 32 for (int j=1;j<=n;j++) { 33 scanf("%lf",&x); 34 a[i][j]=2*(x-b[j]); 35 a[i][n+1]+=x*x-b[j]*b[j]; 36 b[j]=x; 37 } 38 } 39 gauss(); 40 for (int i=1;i<=n;i++) { 41 printf("%.3lf",a[i][n+1]); 42 if (i<n) printf(" "); 43 else puts(""); 44 } 45 return 0; 46 }
2.poj 1222 EXTENDED LIGHTS OUT
解法一、暴力枚举 枚举第一行每个关不关。一行处理完后用这一行的状态去推下一行每个关不关最后check。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 6 int a[10][10],b[10][10],c[10][10],T,cas; 7 8 bool ok() { 9 for (int i=1;i<=6;i++) 10 if (a[5][i]) return false; 11 return true; 12 } 13 void press(int i,int j) { 14 a[i][j]^=1; 15 if (i>1) a[i-1][j]^=1; 16 if (i<5) a[i+1][j]^=1; 17 if (j>1) a[i][j-1]^=1; 18 if (j<6) a[i][j+1]^=1; 19 } 20 21 int main() 22 { 23 scanf("%d",&T); 24 while (++cas<=T) { 25 memset(b,0,sizeof(b)); 26 for (int i=1;i<=5;i++) 27 for (int j=1;j<=6;j++) 28 scanf("%d",&c[i][j]); 29 printf("PUZZLE #%d\n",cas); 30 for (int i=0;i<(1<<6);i++) { 31 for (int j=1;j<=5;j++) 32 for (int k=1;k<=6;k++) 33 a[j][k]=c[j][k]; 34 memset(b,0,sizeof(b)); 35 int pos=i; 36 for (int j=1;j<=5;j++) { 37 int w=0; 38 for (int k=1;k<=6;k++) 39 if (1<<(k-1)&pos) press(j,k),b[j][k]^=1; 40 for (int k=1;k<=6;k++) 41 if (a[j][k]) w|=1<<(k-1); 42 pos=w; 43 } 44 if (ok()) { 45 for (int j=1;j<=5;j++,puts("")) 46 for (int k=1;k<=6;k++) 47 printf("%d ",b[j][k]); 48 break; 49 } 50 } 51 } 52 53 return 0; 54 }
解法二、高斯消元 可以发现对于每个点a[i][j](目前状态),b[i][j]为答案(暂不考虑边界) 有b[i][j]^b[i-1][j]^b[i][j-1]^b[i+1][j]^b[i][j+1]=a[i][j] 就可以建立30个异或方程。 然后解出方程即得答案
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 7 int a[35][35],T,cas; 8 9 void gauss() { 10 for (int i=1;i<=30;i++) { 11 int p=i; 12 while (p<=30&&a[p][i]==0) p++; 13 if (p^i) 14 for (int j=1;j<=31;j++) swap(a[i][j],a[p][j]); 15 for (int k=1;k<=30;k++) { 16 if (i!=k&&a[k][i]) { 17 for (int j=1;j<=31;j++) a[k][j]^=a[i][j]; 18 } 19 } 20 } 21 } 22 int bs(int x){return x>0?x:-x;} 23 24 int main() 25 { 26 scanf("%d",&T); 27 while (++cas<=T) { 28 for (int i=1;i<=30;i++) scanf("%d",&a[i][31]); 29 for (int i=1;i<=30;i++) { 30 int x=(i-1)/6,y=(i-1)%6; 31 for (int j=1;j<=30;j++) { 32 int xx=(j-1)/6,yy=(j-1)%6; 33 a[i][j]=(bs(x-xx)+bs(y-yy))<=1; 34 } 35 } 36 gauss(); 37 printf("PUZZLE #%d\n",cas); 38 for (int i=1;i<=30;i++) { 39 printf("%d ",a[i][31]); 40 if (!(i%6)) puts(""); 41 } 42 } 43 44 return 0; 45 }
3.poj 1830 开关问题
此题跟上题差不多,不过要求一个自由变元的数量r,则ans=1<<r。当然还需判断无解。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 6 int T,n,a[50][50],x,y,s[50],e[50]; 7 8 int gauss() { 9 int cnt=1; 10 for (int i=1;i<=n;i++) { 11 int p=i; 12 while (p<=n&&!a[p][i]) p++; 13 if (a[p][i]) { 14 for (int j=1;j<=n+1;j++) swap(a[cnt][j],a[p][j]); 15 for (int j=1;j<=n;j++) { 16 if (cnt!=j&&a[j][i]) { 17 for (int k=1;k<=n+1;k++) a[j][k]^=a[cnt][k]; 18 } 19 } 20 cnt++; 21 } 22 } 23 for (int i=cnt;i<=n;i++) 24 if (a[i][n+1]) return -1; 25 return n-cnt+1; 26 } 27 28 int main() 29 { 30 scanf("%d",&T); 31 while (T--) { 32 scanf("%d",&n); 33 memset(a,0,sizeof(a)); 34 for (int i=1;i<=n;i++) scanf("%d",&s[i]); 35 for (int i=1;i<=n;i++) { 36 scanf("%d",&e[i]); 37 if (s[i]^e[i]) a[i][n+1]=1; 38 a[i][i]=1; 39 } 40 while (scanf("%d%d",&x,&y)&&x+y) a[y][x]=1; 41 int ans=gauss(); 42 if (ans==-1) puts("Oh,it's impossible~!!"); 43 else printf("%lld\n",1LL<<ans); 44 } 45 46 return 0; 47 }
4.bzoj 1923 外星千足虫
又是同样的异或方程组。 数据范围貌似有点大。要用个bitset。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <bitset> 5 using namespace std; 6 int n,m; 7 bool read() { 8 char c=getchar(); 9 while (c>'1'||c<'0') c=getchar(); 10 return c=='1'; 11 } 12 bitset <1005> a[2005]; 13 14 int gauss() { 15 int ret=0; 16 for (int i=1;i<=n;i++) { 17 int p=i; 18 while (p<=m&&!a[p][i]) p++; 19 if (p>m) return -1; 20 ret=max(ret,p); 21 if (i^p) swap(a[p],a[i]); 22 for (int j=1;j<=m;j++) 23 if (i^j&&a[j][i]) a[j]^=a[i]; 24 } 25 return ret; 26 } 27 28 int main() 29 { 30 scanf("%d%d",&n,&m); 31 for (int i=1;i<=m;i++) 32 for (int j=1;j<=n+1;j++) 33 a[i][j]=read(); 34 int ans=gauss(); 35 if (ans==-1) puts("Cannot Determine"); 36 else { 37 printf("%d\n",ans); 38 for (int i=1;i<=n;i++) { 39 if (a[i][n+1]) puts("?y7M#"); 40 else puts("Earth"); 41 } 42 } 43 return 0; 44 }
5.bzoj 2115 [Wc2011] Xor
链^环=链。找到一条到n链以及所有环。
那么,就是相当于给你一些数,然后Xor出最大的结果就完了。是不是线性基裸题了?
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 typedef long long ll; 6 struct edge { 7 int v,next;ll w; 8 }e[200005]; 9 ll read() { 10 char c=getchar();ll x=0; 11 while (c<'0'||c>'9') c=getchar(); 12 while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 13 return x; 14 } 15 ll ans,p[70],a[200005],f[100005]; 16 int n,m,k,cnt,head[200005],vis[100005]; 17 18 void adde(int u,int v,ll w) { 19 e[k].v=v;e[k].w=w;e[k].next=head[u];head[u]=k++; 20 } 21 void dfs(int u) { 22 vis[u]=1; 23 for (int i=head[u];~i;i=e[i].next) { 24 int v=e[i].v; 25 if (!vis[v]) { 26 f[v]=f[u]^e[i].w;dfs(v); 27 }else a[++cnt]=f[u]^e[i].w^f[v]; 28 } 29 } 30 31 int main() 32 { 33 memset(head,-1,sizeof(head)); 34 n=read();m=read(); 35 for (int i=1;i<=m;i++) { 36 int x,y;ll z; 37 x=read();y=read();z=read(); 38 adde(x,y,z);adde(y,x,z); 39 } 40 dfs(1); 41 for (int i=1;i<=cnt;i++) { 42 for (int j=63;j>=0;j--) { 43 if ((a[i]>>j)&1) { 44 if (!p[j]) {p[j]=a[i];break;} 45 a[i]^=p[j]; 46 } 47 } 48 } 49 ans=f[n]; 50 for (int i=63;i>=0;i--) ans=max(ans,ans^p[i]); 51 printf("%lld\n",ans); 52 return 0; 53 }
6.bzoj 2460 元素
按magic排个序贪心就完了
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 typedef long long ll; 7 int n,ans; 8 ll p[70]; 9 struct E { 10 ll num;int w; 11 bool operator < (const E&b) const { 12 return w>b.w; 13 } 14 }a[1005]; 15 16 int main() 17 { 18 scanf("%d",&n); 19 for (int i=1;i<=n;i++) scanf("%lld%d",&a[i].num,&a[i].w); 20 sort(a+1,a+n+1); 21 for (int i=1;i<=n;i++) { 22 for (int j=63;j>=0;j--) { 23 if (a[i].num&(1ll<<j)) { 24 if (!p[j]) {p[j]=a[i].num;break;} 25 a[i].num^=p[j]; 26 } 27 } 28 if (a[i].num) ans+=a[i].w; 29 } 30 printf("%d\n",ans); 31 return 0; 32 }
7.bzoj 2337 XOR和路径
看到位运算,就按位处理,考虑每一位对答案的贡献w[i],则ans=∑w[i]*(1<<i);
对于每一位:
f[x]表示从x到n二进制下这一位异或和为1的概率
if (w==0) f(u)=∑f(v)/deg[u]
else f(u)=∑(1−f(v))/deg[u]
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <cmath> 5 using namespace std; 6 struct edge { 7 int v,w,next; 8 }e[20005]; 9 const double eps=1e-10; 10 int k,n,m,head[205],d[205]; 11 double ans,a[105][105]; 12 void adde(int u,int v,int w) { 13 e[k].v=v;e[k].w=w;e[k].next=head[u];head[u]=k++; 14 } 15 16 void gauss() { 17 for (int i=1;i<=n;i++) { 18 int p=i; 19 while (p<=n&&fabs(a[p][i])<eps) p++; 20 if (fabs(a[p][i])>eps) { 21 if (i^p) for (int j=1;j<=n+1;j++) swap(a[i][j],a[p][j]); 22 double t=a[i][i]; 23 for (int j=1;j<=n+1;j++) a[i][j]/=t; 24 for (int j=1;j<=n;j++) if (i^j) { 25 t=a[j][i]; 26 for (int k=i;k<=n+1;k++) { 27 a[j][k]-=t*a[i][k]; 28 } 29 } 30 } 31 } 32 } 33 34 int main() 35 { 36 memset(head,-1,sizeof(head)); 37 scanf("%d%d",&n,&m); 38 for (int i=1,u,v,w;i<=m;i++) { 39 scanf("%d%d%d",&u,&v,&w); 40 adde(u,v,w);d[v]++; 41 if (u^v) adde(v,u,w),d[u]++; 42 } 43 for (int i=0;i<=30;i++) { 44 memset(a,0,sizeof(a)); 45 for (int j=1;j<n;j++) { 46 a[j][j]=1.; 47 for (int k=head[j];~k;k=e[k].next) { 48 int v=e[k].v; 49 if (e[k].w>>i&1) a[j][v]+=1./d[j],a[j][n+1]+=1./d[j]; 50 else a[j][v]-=1./d[j]; 51 } 52 } 53 a[n][n]=1.; 54 gauss(); 55 ans+=(1<<i)*a[1][n+1]; 56 } 57 printf("%.3lf\n",ans); 58 return 0; 59 }
8.hdu 4870 Rating
题目大意:小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100(最低为0),告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的那个去做,问她最终有一个账号达到1000分需要做的比赛的次数的期望值。
将0,50,100...1000看做0,1,2...20.
定义f[i][j](i≥j)表示从(i,j)状态到(1000,?)的期望值,则有: f[i][j]=success*p+failure*(1-p)+1;
移项得: f[i][j]-success*p-failure*(1-p)=1; 即可求出ans;
状态有点麻烦,可分i>j和i=j讨论。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 using namespace std; 7 const double eps=1e-10; 8 9 double a[220][220],p; 10 int n,b[25][25]; 11 12 void gauss() { 13 for (int i=1;i<=n;i++) { 14 int p=i; 15 while (p<=n&&fabs(a[p][i])<eps) p++; 16 if (fabs(a[p][i])>eps) { 17 if (i^p) for (int j=1;j<=n+1;j++) swap(a[i][j],a[p][j]); 18 double t=a[i][i]; 19 for (int j=1;j<=n+1;j++) a[i][j]/=t; 20 for (int j=1;j<=n;j++) if (i^j) { 21 t=a[j][i]; 22 for (int k=i;k<=n+1;k++) 23 a[j][k]-=t*a[i][k]; 24 } 25 } 26 } 27 } 28 29 void build() { 30 for (int i=0,t;i<20;i++) { 31 for (int j=0;j<i;j++) { 32 t=b[i][j]; 33 a[t][t]=1;a[t][n+1]=1; 34 a[t][b[i][j+1]]-=p; 35 a[t][b[i][max(j-2,0)]]-=1-p; 36 } 37 t=b[i][i]; 38 a[t][t]=1;a[t][n+1]=1; 39 a[t][b[i+1][i]]-=p; 40 a[t][b[i][max(i-2,0)]]-=1-p; 41 } 42 } 43 44 int main() 45 { 46 for (int i=0;i<20;i++) 47 for (int j=0;j<=i;j++) 48 b[i][j]=++n; 49 while (scanf("%lf",&p)^EOF) { 50 memset(a,0,sizeof(a)); 51 build();gauss(); 52 printf("%.6lf\n",a[1][n+1]); 53 } 54 return 0; 55 }