[ABC276Ex] Construct a Matrix

没有题解,所以来写一篇。

Description

构造一个 N×N 的矩阵 A,其中 Ai,j0,1,2,要求同时满足 Q 条限制。
每条限制形如:给定 a,b,c,d,e,要求 A 满足 i=abj=cdAi,je(mod3)

Solution

为贴合原题,设 (a,b,c,d) 表示左上角为 (a,c),右下角为 (b,d) 的矩形。

根据连乘的性质,若 ei 不为 0,则 (ai,bi,ci,di) 中任意 Ai,j 不能为 0
而对于 ei=0,要求 (a,b,c,d) 中至少一个是 0。那么对于不被 ei{1,2} 限制的点,显然强制它们都为 0 一定不劣。如果这种方式依然不能满足所有 ei=0 的限制,则无解。

现在我们已经确定哪些位置为 0,只需要考虑不为 0 的位置与 ei{1,2} 的限制。而 Ai,j=1 对矩阵积同样没有影响,矩阵积模 3 的值只与 Ai,j=2 的数量有关。

简单找规律(?)可以发现,2 的奇数次幂 mod3 的值为 2,偶数次幂 mod3 的值为 1
考虑把每个 Ai,j 设为未知数解方程。
xi,j 的值 0,1 对应 Ai,j1,2,那么会得到 Q 组方程,形如:

xa,cxa,c+1xb,d=[ei=1]

未知数个数为 n2,直接高斯消元复杂度不能接受。
重定义 xa,b=i=1aj=1bAi,j(mod2),则每个方程可以写成:

xb,dxa1,c1xa1,dxb,c1=[ei=1]

这样每次询问最多会产生 4 个未知数,未知数个数降为 4Q
发现系数只有 0,1,使用 bitset 优化,复杂度降为 O(Q3w),可以通过。

Code

通过研读部分提交记录才得以大致理解做法,代码可能有参考。

#include<bits/stdc++.h>
#define il inline
using namespace std;
il int read()
{
    int xr=0,F=1; char cr;
    while(cr=getchar(),cr<'0'||cr>'9') if(cr=='-') F=-1;
    while(cr>='0'&&cr<='9') 
        xr=(xr<<3)+(xr<<1)+(cr^48),cr=getchar();
    return xr*F;
}
const int N=2005;
int n,q;
struct node{
	int a,b,c,d,e;
};
vector<node> t;
bitset<(N<<2)> now,a[N<<2];
int id[N][N],s[N][N],tot;
il void add(int x,int y) 
{
	if(!x||!y) return;
	if(!id[x][y]) id[x][y]=++tot;
	now[id[x][y]]=1;
}
bool flag;
il void ins()
{
	for(int i=1;i<=tot;i++)
	{
		if(!now[i]) continue;
		if(!a[i][i]) {a[i]=now;return;}
		now^=a[i];
	}
	if(now[0]) {printf("No\n");flag=1;}
}
int ans[N][N];
int main()
{
	n=read(),q=read();
	for(int i=1;i<=q;i++)
	{
		int a=read(),b=read(),c=read(),d=read(),e=read();
		if(!e) t.push_back({a,b,c,d,e});
		else
		{
			s[a][c]++,s[b+1][d+1]++,s[b+1][c]--,s[a][d+1]--;
			now.reset();
			add(a-1,c-1),add(b,d),add(a-1,d),add(b,c-1);
			if(e==2) now[0]=1; ins();
			if(flag) return 0;
		}
	}
	//for(int i=1;i<=tot;i++,cout<<endl) for(int j=1;j<=n+1;j++) cout<<a[i][j]<<" ";
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=!s[i][j];
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+s[i][j];
	for(auto u:t)
		if(s[u.b][u.d]-s[u.b][u.c-1]-s[u.a-1][u.d]+s[u.a-1][u.c-1]==0) {printf("No\n");return 0;}
	
	
	now.reset();
	for(int i=tot;i;i--)
	{
		if(!a[i][i]) continue;
		now[i]=((now&a[i]).count()&1)^(a[i][0]);
	}
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(id[i][j]) ans[i][j]=now[id[i][j]];
	for(int i=n;i;i--) for(int j=n;j;j--) 
		ans[i][j]=ans[i][j]^ans[i-1][j]^ans[i][j-1]^ans[i-1][j-1];
	
	printf("Yes\n");
	for(int i=1;i<=n;i++,printf("\n"))
		for(int j=1;j<=n;j++)
		{
			if(s[i][j]-s[i-1][j]-s[i][j-1]+s[i-1][j-1]) printf("0 ");
			else if(ans[i][j]) printf("2 ");
			else printf("1 ");
		}
	return 0;
}
posted @   樱雪喵  阅读(86)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示