【集训】线性代数基础

题单
不是自己写的,总结的

P3812 【模板】线性基

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int D = 64;
ll w[D];
bool f;

bool ins(ll x) {
    for (int i = D - 1; i >= 0; i--) {
        if ((x >> i) & 1) {
            if (w[i])
                x ^= w[i];
            else {
                w[i] = x;
                return true;
            }
        }
    }

    return false;
}

void simp() {
    for (int i = D - 1; i >= 0; i--) {
        if (w[i]) {
            for (int j = D - 1; j > i; j--)
                if ((w[j] >> i) & 1)
                    w[j] ^= w[i];
        }
    }
}

ll v[D];
ll mxk(ll k) {
    int cnt = 0;

    for (int i = 0; i < D; i++)
        if (w[i] != 0)
            v[cnt++] = w[i];

    if (k >= (1ll << cnt))
        return -1;

    ll ans = 0;

    for (int i = 0; i < cnt; i++)
        if ((k >> i) & 1)
            ans ^= v[i];

    return ans;
}
int main() {
    int n, m;
    cin >> n;

    for (int i = 1; i <= n; i++) {
        ll x;
        cin >> x;
        f |= (!ins(x));
    }

    simp();
    cin >> m;

    for (int i = 1; i <= m; i++) {
        ll k;
        cin >> k;

        if (f)
            k--;

        cout << mxk(k) << endl;
    }

    return 0;
}

LOJ114

题意:n 个数,q 次询问,每次给出 k,求这 n 个数的所有子集的异或和去重后第 k 小的值

我们可以把线性基进一步简化:对每个 i,用最高位为 i 的数去异或上其他所有第 i 位非零的数,这样就可以把线性基进一步简化,它满足:

  • 所有数的最高位互不相同。
  • 对每个 i,如果存在某个 vi 的最高位是第 i 位,则其它数的第 i 位必为 0
    这样的一组线性基 B=v0,,vk,其中 vi 的最高位依次递增,span(B) 中所有元素从小到大排列后恰好就是:0,v0,v1,v0v1,v2,v0v2, ,一直到 v0vk。那么第 k 小也就容易求出了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int D=64;
ll w[D];
bool f;

bool ins(ll x){
	for(int i=D-1;i>=0;i--){
		if((x>>i)&1){
			if(w[i]) x^=w[i];
			else{
				w[i]=x;
				return true;
			}
		}
	} 
	return false;
}

void simp(){
	for(int i=D-1;i>=0;i--){
		if(w[i]){
			for(int j=D-1;j>i;j--)
				if((w[j]>>i)&1) 
					w[j]^=w[i];
		}
	} 
}

ll v[D];
ll mxk(ll k){
    int cnt = 0;
    for (int i = 0; i < D; i++)
        if (w[i] != 0)
            v[cnt++] = w[i];
    if (k >= (1ll << cnt))
        return -1;
    ll ans = 0;
    for (int i = 0; i < cnt; i++)
        if ((k >> i) & 1)
            ans ^= v[i];
    return ans;
}
int main(){
	int n,m;
	cin>>n;
	for(int i=1;i<=n;i++){
		ll x;
		cin>>x;
		f|=(!ins(x));
	}
	
	simp();
	cin>>m;
	for(int i=1;i<=m;i++){
		ll k;
		cin>>k;
		if(f) k--;
		cout<<mxk(k)<<endl;
	}
	return 0;
}

P4570 [BJWC2011] 元素

给出 n 个物品,每个物品有标号 ai 和价值 bi,选出一些物品使得它们的标号不存在一个非空子集异或和为零,且价值之和最大。1n10001ai10181bi104

只需要按价值从大到小考虑,每次能加入就加入即可。
可以用线性基来判断能否插入一个新的元素。时间复杂度 O(n(logV+logn))
证明用一句话来说就是向量空间和它的线性无关子集构成拟阵;当然也可以使用归纳法。

#include <bits/stdc++.h>
using namespace std;
const int D=64,N=1005;
#define ll long long
ll w[D],n;

struct s{
	ll a,b;
}a[N];

bool cmp(s a,s b){
	return a.b>b.b;
}

bool ins(ll x){
	for(int i=D-1;i>=0;i--){
		if((x>>i)&1){
			if(w[i]) x^=w[i];
			else{
				w[i]=x;
				return true;
			} 
		} 
	} 
	return false;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].a>>a[i].b;
	}
	sort(a+1,a+n+1,cmp);
	ll ans=0;
	for(int i=1;i<=n;i++){
		if(ins(a[i].a)) ans+=a[i].b;
	}
	
	cout<<ans<<endl;
	return 0;
}

P4151 [WC2011] 最大XOR和路径

给定一张 nm 边的无向图,边有边权 wi,求一条 1n 的路径(不一定是简单路径),使得路径上边权 XOR 和最大。1n50000,1m105,0wi1018

任取生成树 T 和点 r 为根,设 f(u)ur 路径上边权异或和。
考虑任意一个 1n 的路径 P=(u1,,u)k),设 w(u,v)(u,v) 这条边的边权,则这条路径的权值是

i=1k1w(ui,ui+1)=f(1)f(n)(i=1k1f(ui)w(ui,ui+1f(ui+1)))

注意到当 (ui,ui+1) 是树边的时候,总有 f(ui)w(ui,ui+1)f(ui+1)=0
因此这个式子就等于:

f(1)f(n)((ui,u)Tf(ui)f(ui+1)w(ui,ui+1))

而当 (u,v)T 的时候,f(u)f(v)w(u,v) 的含义就是 (u,v) 以及它们树上路径构成的这个环。

于是,1n 的所有路径,它的边权异或和一定等于 1n 在树上的路径的边权异或和,以及某些仅包含一条非树边的环的边权异或和。

另一方面,不管当前在哪个点 u,假设有一个环 C,我们总是可以从当前点走到 C 中的任意一点 v,绕着 C 走一圈后回到 v,再回到 u,这期间 uv路径被走了两遍,相当于没有;而我们的边权异或和只多出来了 C 上的那一部分。

综上,我们证明了所有 1n 的路径上边权异或和,就是 1n 在树上的路径的边权异或和,以及任意包含恰好一条非树边的环的边权异或和线性组合后的结果。

那么对后者建出线性基,查询前者在后者中的最大异或值即可,总复杂度 O(n+(mn)logV)

#include <bits/stdc++.h>
using namespace std;
const int N=50005,M=200005,D=64;
#define ll long long
ll h[N],e[M],ne[M],W[M],idx;
ll f[N],w[D];
bool vis[N]; 

bool ins(ll x){
	for(int i=D-1;i>=0;i--) {
		if((x>>i)&1){
			if(w[i]) x^=w[i];
			else{
				w[i]=x;
				return true; 
			} 
		} 
	}
	return false;
}

void simp(){
	for(int i=D-1;i>=0;i--){
		if(w[i]!=0){
			for(int j=D-1;j>i;j--)
				if((w[j]>>i)&1) w[j]^=w[i];
		}
	} 
}

ll mx(ll x){
	ll ans=x;
	for (int i=D-1;i>=0;i--)
        ans=max(ans,ans^w[i]);
	return ans;
}
void add(int u,int v,ll w){
	e[idx]=v,W[idx]=w,ne[idx]=h[u],h[u]=idx++;
}

void dfs(int u,ll ans){
	f[u]=ans,vis[u]=1;
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		if(!vis[j]) dfs(j,ans^W[i]);
		else ins(ans^W[i]^f[j]);
	}
} 
int main(){
	memset(h,-1,sizeof(h));
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		ll u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);add(v,u,w);
	} 
	
	dfs(1,0);
	simp();
	cout<<mx(f[n])<<endl;
	return 0;
}

CF724G Xor-matic Number of the Graph

n 个点的无向图,有边权。定义三元组 (u,v,w)(1u<vn) 合法当且仅当存在从点 u 到点 v 存在一条边权异或和为 w 的路径,经过多次的边需要算多次。求所有合法三元组的 w 值之和对 109+7 取模的值。

uv 所有路径的异或和等于 dudvB,其中 dx 表示 x 到根的路径的异或和。

对于一对 (u,v),尝试统计 dudvB 中所有数的和。

直接做并不是很好做,考虑按位分开做:

  • 对于线性基 B 和二进制位 w,有结论:
  • B 中元素个数为 S,则 $B¥ 可以表示出 2S 个不同的数。
  • 如果 B 中存在二进制第 w 位为 1 的数,则那 2S 个数中恰有 2S1 个数的二进制第 w 位为 1,另外 2S1 个数的二进制第 w 位为 0
  • 如果 B 中不存在二进制第 w 位为 1 的数,显然不可能表示出二进制第 w 位为 1 的数,全部 2S 个数的二进制第 w 位均为 0
    可以通过组合恒等式 i=0n(ni)[imod2=1]={0,n=02n1,n>0 证明。

枚举二进制位 w,考虑线性基 B 中是否存在二进制第 w 位为 1 的数。

如果存在,这意味着无论 du,dv 的二进制第 w 位是否为 1,都恰有 2S1 条使得异或和的二进制第 w 位为 1 的路径。这意味着 u,v 可以随便选,对答案的贡献为 2w2S1(n2)

如果不存在,这意味着 du,dv 的二进制第 w 位必须恰有一个为 1,并且此时存在 2S 条使得异或和的二进制第 w 位为 1 的路径。
这意味着 du,dv 的第 w 位必须恰有一个为 1,记第 w 位为 1dx 的个数为 x,对答案的贡献为 2w2Sx(nx)

对于每个联通块分别计算即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int D=64,N=100005,M=N*4,P=1e9+7;
ll h[N],e[M],ne[M],W[M],idx;
ll w[D],f[N],s[N],C,t;
bool vis[N];
 
bool ins(ll x){
	for(int i=60;i>=0;i--){
		if((x>>i)&1){
			if(w[i]) x^=w[i];
			else{
				w[i]=x;
				++C; 
				return true;
			}
		} 
	} 
	return false;
}
void add(int u,int v,ll w){
	e[idx]=v,W[idx]=w,ne[idx]=h[u],h[u]=idx++;
}
void dfs(int u,ll ans){
	f[u]=ans,vis[u]=1,s[++t]=u;
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		if(!vis[j]) dfs(j,ans^W[i]);
		else ins(ans^W[i]^f[j]); 
	}
}
signed main(){
	memset(h,-1,sizeof(h));
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		ll u,v,w;
		cin>>u>>v>>w;
		add(u,v,w),add(v,u,w);
	}
	
	ll ans=0;
	for(int i=1;i<=n;i++){
		if(vis[i]==0){
			memset(w,0,sizeof(w));
			C=t=0;
			dfs(i,0);
			
			for(int j=0;j<60;j++){
				ll c=(1ll<<j)%P;
				bool ok=0;
				for(int k=60;k>=0;k--) if((w[k]>>j)&1) ok=1;
				if(ok) ans=(ans+(ll)t*(t-1)/2%P*((1ll<<C-1)%P)%P*c)%P;
				else{
					int x=0;
					for(int i=1;i<=t;i++) if((f[s[i]]>>j)&1) ++x;
					ans=(ans+(ll)x*(t-x)%P*((1ll<<C)%P)%P*c)%P;
				}
			} 
		}
	}
	
	cout<<ans%P<<endl;
	return 0;
}

P10682 [COTS 2024] 奇偶南瓜 Tikvani

给定 n 个点 m 条边的 DAG,求有多少种给每条边赋 0/1 权值的方式使得任意两条起终点相同的路径权值和 mod2 都同余。n,m400

对每个起点 u 分别考虑,根据经典结论,先求出一棵以 u 为根的外向 dfs 树,设树上 uv 的路径为 fu,那么只要考虑恰经过一条非树边的环。

即对于一条非树边 xy,我们只要求 fxfyw(xy)=0,并且不难证明这是充分的。

对于任意一条路径,找到其中的第一条非树边 xy,根据限制,可以把到 y 的路径等效成 fy,递归进行此过程即可证明该路径合法。

那么此时总共只有 O(nm) 条限制,暴力插入并用 bitset 优化高斯消元维护。

时间复杂度 O(nm3ω)

#include<bits/stdc++.h>
using namespace std;
const int N=405,P=1e9+7;
int n,m;
struct s{int v,id;};
vector<s> G[N];
bitset <N> w,d[N],x[N];
bool vis[N];
void ins() {
	for(int i=1;i<=m;++i) if(w[i]) {
		if(!x[i].any()) 
			return x[i]=w,void();
		else w^=x[i];
	}
}
void dfs(int u) {
	vis[u]=true;
	for(auto e:G[u]) {
		if(!vis[e.v]) d[e.v]=d[u],d[e.v].set(e.id),dfs(e.v);
		else w=d[u],w^=d[e.v],w.flip(e.id),ins();
	}
}
signed main() {
	cin>>n>>m;
	for(int i=1,u,v;i<=m;++i) cin>>u>>v,G[u].push_back({v,i});
	for(int i=1;i<=n;++i) {
		for(int u=1;u<=n;++u) 
			d[u].reset(),vis[u]=false;
		dfs(i);
	}
	int ans=1;
	for(int i=1;i<=m;++i) if(!x[i].any()) ans=ans*2%P;
	cout<<ans<<endl;
	return 0;
}

与数据结构配合的线性基

  • 线性基插入的复杂度是 O(logV)
  • 线性基合并的复杂度是 O(log2V),方法是把其中一个的所有元素插入到另一边。

CF1100F Ivan and Burgers

给一个长为 n 的序列 aq 次询问,问从 al,al+1,,ar 中选若干个数,异或和最大为多少。1n,q5×105,0ai10

在一些数中取若干个数,要求异或和最大,不难想到线性基。

直接线段树维护区间,则复杂度达到 O((n+q)log3n),用ST表的话,预处理复杂度也达到了 O(nlog3n),难以接受。

考虑离线,然后分治。每次考虑解决经过当前区间中点的询问。

对于一个区间,我们处理出中点mid往左的后缀线性基、往右的前缀线性基,则可以在 O(log2n)(线性基合并)的复杂度解决一个经过中点的询问。

然后,对于两端点都在左边的区间,往左递归处理;两端点都在右边的区间,往右递归处理。

分治时间复杂度:T(n)=2T(n2)+O(nlogn)=O(nlog2n)。加上处理所有询问的复杂度 O(qlog2n),总复杂度 O((n+q)log2n)

#include <bits/stdc++.h>
using namespace std;
const int N=500005;
struct ss{
	int s[20];
	void ins(int x){
		if(x)
		for(int i=19;i>=0;i--)
		if(x>>i&1){
			if(!s[i]) {s[i]=x;break;}
			x^=s[i];
		}
	}
	void clear(){
		memset(s,0,sizeof(s));
	}
}d[N];
 
int merge(ss a,ss b){
	for(int i=19;i>=0;i--) a.ins(b.s[i]);
	int res=0;
	for(int i=19;i>=0;i--) 
		res=max(res,res^a.s[i]);
	return res; 
} 
 
int a[N],n,q[N],qL[N],qR[N],ans[N],m;
 
void solve(int l,int r,int L,int R){
 
	if(L>R) return;
	if(l==r){
		for(int i=L;i<=R;i++) ans[q[i]]=a[l];
		return;
	}
	static int tmpL[N],tmpR[N];
	int tL=0,tR=0,mid=l+r>>1;
	d[mid].clear();d[mid].ins(a[mid]);
	for(int i=mid-1;i>=l;i--) (d[i]=d[i+1]).ins(a[i]);
	for(int i=mid+1;i<=r;i++) (d[i]=d[i-1]).ins(a[i]);
	for(int i=L;i<=R;i++){
		if(qL[q[i]]<=mid){
			if(qR[q[i]]>mid) ans[q[i]]=merge(d[qL[q[i]]],d[qR[q[i]]]);
			else tmpL[++tL]=q[i]; 
		}
		else tmpR[++tR]=q[i];
	}
	for(int i=1;i<=tL;i++)q[L+i-1]=tmpL[i];
	for(int i=1;i<=tR;i++)q[L+tL+i-1]=tmpR[i];
	solve(l,mid,L,L+tL-1);
	solve(mid+1,r,L+tL,L+tL+tR-1);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	cin>>m;
	for(int i=1;i<=m;i++)cin>>qL[i]>>qR[i],q[i]=i;
	solve(1,n,1,m);
	for(int i=1;i<=m;i++)cout<<ans[i]<<endl;
	return 0; 
}

P3292 [SCOI2016] 幸运数字

按照倍增的思想,gi,j 表示i往上跳 2j 步的线性基,然后 dp 的时候暴力合并两个 gi,j1gfai,j1,j1,复杂度 O(NlogN×64)

#include<bits/stdc++.h>
using namespace std;
const int M=20005,D=64;
typedef long long ll;
ll a[100005],n;
struct s{
	ll w[64];
	s(){ memset(w,0,sizeof(w));	}
	void insert(ll x){
		for(int i=D-1;i>=0;i--){
			if(!((x>>i)&1)) continue;
			if(!w[i]){w[i]=x;return ;}
			x^=w[i];
		}
	}
	ll mx(){
		ll ans=0;
		for(int i=D-1;i>=0;i--)
			ans=max(ans,ans^w[i]);
		return ans;
	}
};
s merge(s u,s v){
	for(int i=D-1;~i;i--)
		if(v.w[i]) u.insert(v.w[i]);
	return u;
}
int idx,ne[2*M],h[2*M],e[2*M];
void add(int x,int y){
	e[++idx]=y,ne[idx]=h[x],h[x]=idx;
	
}
int f[M][21],dep[M];
s g[M][21];
void dfs(int u,int fa){
	f[u][0]=fa;
	g[u][0].insert(a[fa]);
	dep[u]=dep[fa]+1;
	for(int i=h[u];i;i=ne[i]){
		int to=e[i];
		if(to!=fa) dfs(to,u);
	}
}
ll query(int x,int y){
	if(dep[x]>dep[y])swap(x,y);
	s t;
	t.insert(a[x]),t.insert(a[y]);
	for(int i=20;i>=0;i--){
		if(dep[f[y][i]]>=dep[x]) t=merge(t,g[y][i]),y=f[y][i];
	}
	if(x==y)return t.mx();
	for(int i=20;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			t=merge(t,g[x][i]);
			t=merge(t,g[y][i]);
			x=f[x][i],y=f[y][i];
		}
	}
	t=merge(t,g[x][0]);
	return t.mx();
}
int main(){
	int q;
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		add(x,y),add(y,x);
	}
	dfs(1,0);
	for(int j=1;j<=20;j++){
		for(int i=1;i<=n;i++){
			f[i][j]=f[f[i][j-1]][j-1];
			g[i][j]=merge(g[f[i][j-1]][j-1],g[i][j-1]);
		}
	}
	while(q--){
		int x,y;
		cin>>x>>y;
		cout<<query(x,y)<<endl;
	}
	return 0;
}

P5607 [Ynoi2013] 无力回天 NOI2017

由于要求维护区间信息,采用线段树维护区间线性基。

考虑差分,将区间修改转化为两个单点修改。接下来考虑如何在差分后的序列上求答案。

序列 b 满足 bi=aiai1,则有:ax=b1b2b3bx

不难发现,对于 a 序列的区间 [l,r],其中任意一种数字选选取方案都可以用 al,bl+1,bl+2,bl+3...br 中选取若干数表示出来,即序列 al,al+1,...ar 的线性基与al,bl+1,bl+2,...br 相同。

查询时,求出 bl+1,bl+2,bl+3...br 的线性基和 al,将 al 插入线性基,贪心统计答案即可。

线段树上合并两个线性基复杂度O(log2V),因此总复杂度为O(nlog+nlog2V)

#include <bits/stdc++.h>
#define L(u) (u<<1)
#define R(u) (u<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N = 50050;
struct XOR {
	int d[30], s;
	void init() {memset(d, 0, sizeof d);}
	void insert(int x) {
		for(int i = 29; i >= 0; --i)
			if((x >> i) & 1) {
				if(d[i]) x ^= d[i];
				else {d[i] = x; break;}
			}
	}
} t[N << 2]; int a[N], n, m;
XOR merge(XOR a, XOR b) {
	XOR res; res.init();
	for(int i = 29; i >= 0; --i) if(a.d[i]) res.d[i] = a.d[i]; else res.d[i] = b.d[i];
	for(int i = 29; i >= 0; --i) if(a.d[i] && b.d[i]) res.insert(b.d[i]);
	res.s = a.s ^ b.s; return res;
}
void P(int u) {t[u] = merge(t[L(u)], t[R(u)]);}
void build(int u, int l, int r) {
	if(l == r) {t[u].insert(t[u].s = a[l]); return;}
	build(L(u), l, mid); build(R(u), mid+1, r); P(u);
}
void modify(int u, int l, int r, int p, int x) {
	if(l == r) {t[u].init(), t[u].insert(t[u].s ^= x); return; }
	p <= mid ? modify(L(u), l, mid, p, x) : modify(R(u), mid+1, r, p, x); P(u);
}
int finds(int u, int l, int r, int p) {
	return l == r ? t[u].s : (p <= mid ? finds(L(u), l, mid, p) : finds(R(u), mid+1, r, p) ^ t[L(u)].s);
}
XOR findi(int u, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) return t[u];
	if(tl > mid) return findi(R(u), mid+1, r, tl, tr);
	if(tr <= mid) return findi(L(u), l, mid, tl, tr);
	return merge(findi(L(u), l, mid, tl, tr), findi(R(u), mid+1, r, tl, tr));
}
int main() {
	cin>>n>>m; 
	for(int i = 1; i <= n; ++i) cin>>a[i];
	for(int i = n-1; i; --i) a[i+1] ^= a[i]; 
	build(1, 1, n);
	for(int opt, l, r, x; m--; ) {
		cin>>opt>>l>>r>>x;
		if(opt == 1) {
			modify(1, 1, n, l, x);
			if(r < n) modify(1, 1, n, r+1, x);
		} else {
			int tmp = finds(1, 1, n, l);
			if(l == r) cout<<max(x, x^tmp)<<endl;
			else {
				XOR t = findi(1, 1, n, l+1, r); t.insert(tmp);
				for(int i = 29; i >= 0; --i) x = max(x, x ^ t.d[i]);
				cout<<x<<endl;
			}
		}
	}
}

P3389 【模板】高斯消元法

#include<bits/stdc++.h>
using namespace std;
int n,pl;
double a[1001][1001];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
       for(int j=1;j<=n+1;j++)
        cin>>a[i][j];
    for(int i=1;i<=n;i++){
        pl=i;
        for(int j=i;j<=n;j++)
        	if(fabs(a[j][i])>fabs(a[pl][i])) pl=j;
        if(a[pl][i]==0) {cout<<"No Solution";return 0;} 
        for(int j=1;j<=n+1;j++)
        swap(a[i][j],a[pl][j]);
        double k=a[i][i];
        for(int j=1;j<=n+1;j++)
        a[i][j]=a[i][j]/k; 
        for(int j=1;j<=n;j++){
            if(i!=j){ 
                double ki=a[j][i];
                for(int m=1;m<=n+1;m++)
                    a[j][m]=a[j][m]-ki*a[i][m];
            }
        }
    }
    for(int i=1;i<=n;i++)
        printf("%.2lf\n",a[i][n+1]);
    return 0;
}

P2447 [SDOI2010] 外星千足虫

一种方法是直接使用线性基:方程组有唯一解相当于 rk(S)=n,而一个矩阵的秩就是把所有元素插入线性基之后,线性基中元素的个数。于是从前往后把

所有方程插入线性基即可,时间复杂度 O(n2mω)

另一方面,我们也可以使用高斯消元,不过在消元的时候尽可能选编号更小的方程来进行操作;这本质上和线性基是相同的,相当于快速找到了下一次线性

基发生改变的位置。

#include <bits/stdc++.h>
using namespace std;
char s[1010];
bitset<1010> a[2010]; 
int f(int n, int m){
    int ans = -1; 
    for (int i = 1; i <= n; i++){
        int cur = i;
        while (cur <= m && !a[cur].test(i)) cur++;
        if (cur > m) return 0;
        ans = max(ans, cur); 
        if (cur != i)swap(a[cur], a[i]);
        for (int j = 1; j <= m; j++)
            if (i != j && a[j].test(i)) 
                a[j] ^= a[i];
    }
    return ans;
}
int main(){
    int n, m;
   	cin>>n>>m;
    for (int i = 1, res; i <= m; i++){
        cin>>s>>res;
        for (int j = 0; j < n; j++)
            a[i].set(j+1,s[j]=='1');
        a[i].set(0, res);
    }
    int ret = f(n, m); 
    if (ret){
        cout<<ret<<endl;
        for (int i = 1; i <= n; i++)
            cout<<(a[i].test(0) ? "?y7M#" : "Earth") <<endl;
    }
    else cout<<"Cannot Determine\n"<<endl;
    return 0;
}

CF24D Broken robot

nm 列的矩阵,现在在 (x,y),每次等概率向左,右,下走或原地不动,但不能走出去,问走到最后一行期望的步数。

注意,(1,1) 是木板的左上角,(n,m) 是木板的右下角。1n,m1031xn1ym

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <iomanip>
 
using namespace std;
 
const int N = 1010;
 
int n, m;
int x, y;
double f[N][N];
double a[N][N];
 
void gauss()
{
    for (int i = 1; i <= m; i ++ )
    {
        double  t = a[i + 1][i] / a[i][i];
        int d[3] = {i, i + 1, m + 1};
        for (int j = 0; j < 3; j ++ )
            a[i + 1][d[j]] -= t * a[i][d[j]];
        a[i + 1][i] = 0;
    }
 
    for (int i = m; i; i -- )
    {
        a[i - 1][m + 1] -= a[i - 1][i] / a[i][i] * a[i][m + 1];
        a[i - 1][i] = 0;
    }
}
 
int main()
{
    cin >> n >> m;
    cin >> x >> y;
 
    if (m == 1) printf("%.4lf\n", 2.0 * (n - x));
    else
    {
        for (int i = n - 1; i >= x; i -- )
        {
            a[1][1] = 2.0 / 3, a[1][2] = -1.0 / 3, a[1][m + 1] = f[i + 1][1] / 3 + 1;
            a[m][m] = 2.0 / 3, a[m][m - 1] = -1.0 / 3, a[m][m + 1] = f[i + 1][m] / 3 + 1;
            for (int j = 2; j < m; j ++ )
            {
                a[j][j - 1] = -1.0 / 4, a[j][j] = 3.0 / 4, a[j][j + 1] = -1.0 / 4;
                a[j][m + 1] = f[i + 1][j] / 4 + 1;
            }
 
            gauss();
 
            for (int j = 1; j <= m; j ++ ) f[i][j] = a[j][m + 1] / a[j][j];
        }
 
        cout.setf(std::ios::fixed);
        cout << setprecision(4) << f[x][y];
        //cout << f[x][y];
    }
 
    return 0;
}
posted @   Star_F  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示