[省选集训2022] 模拟赛8

A

题目描述

给定一个 \(n\times m\)\(01\) 矩阵,对于矩阵的每一个位置,你需要对于这个位置上的值反转,然后求出这个矩阵的秩的变化:+,-,0

可以将这个矩阵看成 \(n\) 个大小为 \([0,2^m)\) 的数,秩就是它们线性基的大小。

\(n,m\leq 1000\)

解法

我们判断求出原先的 \(n\) 个向量在原来的线性基中是可以替代的还是不可替代的,这个可以记录一下线性基每个向量对应的位置,然后求出把新加入的向量消成 \(0\) 需要用到哪些位置,那么这些位置都是可以替代的,下面可以分这两类讨论:

如果这个向量是可以替代的,那么反转 \(j\) 位可以考虑成新增一个 \(2^j\) 向量,这和原来的向量是独立的,原来的向量还是可以被凑出来。那么我们只需要考虑这个新增的向量能否被凑出来即可,对于每一位可以预处理这一位是否被凑出来。如果是那么位为 0;否则为 +

如果这个向量是不可替代的,那么考虑如果原线性基不能凑出 \(2^j\) 秩一定是 0,然后如果凑出 \(2^j\) 包含这个向量,说明这个向量是必须使用的,而现在又要将其删除后再添加,所以答案是 0;否则如果不必须使用这个向量那么答案为 -

可以用 \(\tt bitset\) 优化,时间复杂度 \(O(\frac{nm^2}{w})\)

#include <cstdio>
#include <bitset>
using namespace std;
const int M = 1005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ir[M],ok[M];bitset<M> b[M],p[M],use[M];
signed main()
{
	freopen("math.in","r",stdin);
	freopen("math.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++)
	{
		bitset<M> a,nw;a[i]=nw[i]=ir[i]=1;
		for(int j=1,x;j<=m;j++)
			scanf("%1d",&x),a[j]=x;
		int f=0;
		for(int j=m;j>=1;j--)
		{
			if(!a[j]) continue;
			if(a[j] && !b[j][j])
				{f=1;b[j]=a;p[j]=nw;break;}
			a=a^b[j];nw=nw^p[j];
		}
		if(!f) for(int j=1;j<=n;j++)
			if(nw[j]) ir[j]=0;
	}
	for(int i=1;i<=m;i++)
	{
		bitset<M> nw;nw[i]=ok[i]=1;
		for(int j=m;j>=1;j--)
		{
			if(!nw[j]) continue;
			if(nw[j] && !b[j][j])
				{ok[i]=0;break;}
			nw=nw^b[j];use[i]=use[i]^p[j];
		}
	}
	for(int i=1;i<=n;i++,puts(""))
		for(int j=1;j<=m;j++)
		{
			if(ir[i])//irreplacable
			{
				if(!ok[j] || !use[j][i]) putchar('0');
				else putchar('-');
			}
			else
			{
				if(!ok[j]) putchar('+');
				else putchar('0');
			}
		}
}

B

题目描述

\(n\) 个点 \(m\) 条线段,其中:每条线段的端点都在这 \(n\) 个点之中;任意两条线段只在这 \(n\) 个点上相交;每条线段只会在端点处经过这 \(n\) 个点;每条线段要么是竖直的,要么斜率为 \(1,0,-1\)

现在共有 \(k\) 种颜色,将它们分别标号为 \(1...k\),你想要将每个点染成这 \(k\) 种颜色中的一种,使得不存在被某条线段连接的两个点颜色相同。

\(n\leq 10^4,m\leq 2\cdot 10^4,k=4\)

解法

介绍一种较好的搜索方法,我们可以把问题转化成把原图划分成两个导出子图使得它们都是二分图,那么如果出现奇环把奇环上的点调整到另一个到处子图上即可。

当然本题直接暴搜也是能过的,如果您有更好的方法请联系我

#include <cstdio>
#include <vector>
#include <cstdlib>
using namespace std;
const int M = 10005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,c[M],vis[M][10];vector<int> g[M];
void zxy(int u)
{
	if(u==n+1)
	{
		for(int i=1;i<=n;i++)
			printf("%d\n",c[i]);
		exit(0);
	}
	for(int i=1;i<=k;i++) if(!vis[u][i])
	{
		for(auto v:g[u]) vis[v][i]++;
		c[u]=i;zxy(u+1);
		for(auto v:g[u]) vis[v][i]--;
	}
}
signed main()
{
	freopen("construct.in","r",stdin);
	freopen("construct.out","w",stdout);
	n=read();m=read();k=read();
	for(int i=1;i<=n;i++) read(),read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g[min(u,v)].push_back(max(u,v));
	}
	zxy(1);return 0;
}
posted @ 2022-02-16 23:02  C202044zxy  阅读(94)  评论(0编辑  收藏  举报