【2022国赛模拟】[ZROI1778]矩阵——构造

ZR省选十连测day6 矩阵

题目描述

在这里插入图片描述
在这里插入图片描述

题解

正解是刚好卡到 26 次的,说一个最多 24 次的做法。

首先考虑只要求对角线为 0 的情况,相当于要把所有 i ≠ j i\neq j i=j 的位置填上。考虑一次操作相当于一个行标和列标匹配的过程,我们可以考虑用类似CDQ分治的方法,在线段树上每次让前一半标号做行,后一半标号做列,这样可以在 2 n 2n 2n 次操作内刚好填满所有 i < j i<j i<j 的位置,而 i > j i>j i>j 的位置反过来做即可。

然后发现这个过程其实很浪费,可以让线段树上同一深度的操作同时做,也就是并行计算,因为我们只要 i ≠ j i\neq j i=j 嘛。这样操作次数就是 log ⁡ n \log n logn 了,其本质上和二进制分组是一样的。

然后考虑这道题的情况,对角线下面还有一条 0。这其实就是把我们原来要填的 i > j i>j i>j 的下三角往下挪了一格,如果我们是原始的CDQ分治做法的话,可以简单地在做第二遍的时候把它简单重标号一下,然后判掉出界的行标即可。

但是直接沿用到并行计算上面会有漏洞,因为把区间的操作并起来的时候会出现一个列标号区间拼在一个行标号区间前面,这样填出来就不合法。
在这里插入图片描述
正解的解决方法是把原矩阵奇偶分类(或叫黑白染色?),然后另找他法。其实不需要的,我们可以直接把两遍操作过程穿插在一起:
在这里插入图片描述
注意到重标号仅仅是把行标号-1,所以上面的过程全部换成原标号是这样的:
在这里插入图片描述
这样显然不会填到两条线,而且其它位置全都会填满。

总共操作次数是 2 log ⁡ n 2\log n 2logn,最大为 24。

代码

#include<bits/stdc++.h>//JZM yyds!!
#define ll long long//God JZM!!
#define lll __int128//JZM RollInDark!!
#define uns unsigned
#define fi first
#define se second
#define IF (it->fi)
#define IS (it->se)
#define lowbit(x) ((x)&-(x))
#define END putchar('\n')
#define inline jzmyyds
using namespace std;
const int MAXN=3005;
const ll INF=1e16;
ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int ptf[50],lpt;
void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	ptf[lpt=1]=x%10;
	while(x>9)x/=10,ptf[++lpt]=x%10;
	while(lpt)putchar(ptf[lpt--]^48);
	if(c>0)putchar(c);
}
int n,tot,Q;
#define pii pair<int,int>
vector<pii>T,F;
struct step{set<int>r,c;};
vector<step>as;
int main()
{
	n=read(),Q=read();
	T.emplace_back(pii(1,n));
	while(!T.empty()){
		for(int o=0,e;o<2;o++){
			e=o,as.resize(as.size()+1);
			auto&st=as.back();
			for(auto&lr:T){
				int l=lr.fi,r=lr.se,mid=(l+r)>>1;
				if(e^=1){
					for(int i=l;i<=mid;i++)st.r.insert(i);
					for(int i=mid+1;i<=r;i++)st.c.insert(i);
				}else{
					for(int i=l;i<=mid;i++)st.c.insert(i);
					for(int i=mid+1;i<=r;i++)if(i<n)st.r.insert(i+1);
				}
			}
			if(st.r.empty()||st.c.empty())as.pop_back();
		}
		F.clear();
		for(auto&lr:T){
			int l=lr.fi,r=lr.se,mid=(l+r)>>1;
			if(mid>l)F.emplace_back(pii(l,mid));
			if(r>mid+1)F.emplace_back(pii(mid+1,r));
		}T.swap(F);
	}
	print(as.size());
	for(auto&x:as){
		print(x.r.size(),' '),print(x.c.size(),0);
		for(int z:x.r)printf(" %d",z);
		for(int z:x.c)printf(" %d",z);
		END;
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值