题目描述

\(\sf Oxide\) 拥有着一片大肠树森林。她非常爱整洁,所以这片森林是 \(n\)\(m\) 列的,其中第 \(i\) 行第 \(j\) 列的大肠树的肠数是 \(w_{i,j}\)

\(\sf Oxide\) 有时会去看看她精心培育的大肠树森林,数数树上的肠数。如果一个矩形包含 \(i\)\(j\) 列,且矩形内的肠数最大值减去肠数最小值为 \(i\times j-1\),她数的肠数就会增加 \(i\times j\)

现在 \(\sf Oxide\) 想知道,如果她把大肠树森林中的所有矩形巡视一次,总共能获得多大的肠数。由于 \(\sf Oxide\) 的肠数实在是太大了,你只需要输出答案对 \(998244353\) 取模的结果。

注意,所有的肠数 互不相同,且 \(1\le w_{i,j}\le n\times m\)

解法

以下是博主的碎碎念,不想看的可以跳过。


也不知是怎的,先开始对着 \(n\times m\le 10^4\) 的部分分看了半天。模拟颅内思考:枚举矩形就是 \(10^8\),可是我还要求矩形的 \(\min,\max\) 啊!怎么办啊怎么办啊怎么办啊!当时还想过用矩形的权值和来判断,后来发现枚举的时候就可以顺带算了… 感觉自己又脑瘫了。

然后开始想 \(n=1\) 的部分分。想做一个双指针,增大 \(r\) 时查看对 \(l\) 的改变,但是发现相邻状态根本就没有继承性啊!然后我就死了。


\(\text{Step 1}\)\(n=1\)

从小到大枚举 \(r\),那么 \([l,r]\) 合法的条件就是 \(\max_{l,r}-\min_{l,r}=r-l+1\)

由于这类型的维护可以维护 \([l,r]\)\(l\) 自带的数据,我们不妨将单独的 \(r\) 割裂出来,令 \(f(l)=\max_{l,r}-\min_{l,r}+l\)。数字是互不相同的,所以有 \(f(l)\ge r+1\),所以我们只需要用线段树维护 \(f(l)\)\(\min\),以及 \(f(l)=\min\)\(l\) 的个数,\(l\) 的和。

现在问题变成了维护 \(\max_{l,r},\min_{l,r}\),这个可以用单调栈维护。具体一点,\(\max\) 对应的单调栈维护一个递减序列,当新增一个比序列末尾更大的数 \(x\) 时进行弹栈,更新 \(\max\) 是弹出数字的 \(l\)\(\max\)\(x\)

这是 \(\mathcal O(m\log m)\) 的。

\(\text{Step 2}\)\(n\le 2\times 10^5\)

沿袭上文的做法,我们可以枚举矩形的上下边界 \([L,R]\),这样每次新增 \(r\),插入的数就不是 \(w_r\),而是 \(\max_{i=L}^R w_{i,r}\),除此之外,合法条件的 \(r-l+1\) 应该还要再乘一个系数。

这样的复杂度看上去爆炸,实际上我们还可以做一点优化。令 \(n\)\(n,m\) 中较小的那个数,这样我们可以保证 \(n\le \sqrt{2\times 10^5}\),上下边界在 \(n\) 上枚举。令 \(V=2\times 10^5\),总复杂度是 \(\mathcal O(V\sqrt V\cdot \log m)\)。不过这肯定是过不去的。

题解给出了一种非常 \(\rm nb\) 的做法。

对于大肠树森林,我们在它的外面围一圈大肠树,这样我们就得到了 \((n+1)\cdot (m+1)\) 个框,每个框里有 \(4\) 棵大肠树(一个框被它的左上角表示)。假设枚举了 权值 区间 \([l,r]\),将肠数在区间内的大肠树染黑,其他都是白色。那么它合法的充要条件就是恰好有 \(4\) 个框有 \(1\) 棵黑色大肠树,其它框都是 \(2/4\) 棵黑色大肠树。

还是从小到大枚举 \(r\),记 \(f(l)\) 为将权值区间 \([l,r]\) 染色,有多少个框内有 \(1/3\) 棵黑色大肠树。由于 \(f(l)\ge 4\),所以我们还是只需要用线段树维护 \(f(l)\)\(\min\),以及 \(f(l)=\min\)\(l\) 的个数,\(l\) 的和。

新增 \(r\) 时,只有 \(4\) 个框改变。对于每个框,更改一下 \(l\) 的区间即可。具体就是将框内的数排序,如果新增的 \(x\) 等于其中某个数,说明有些 \(l\) 区间的黑色大肠树个数的奇偶性改变。

这是 \(\mathcal O(nm\log nm)\) 的,但是评测姬真的贼慢,需要卡一卡。

给出一些温馨提示:

  • 不要用结构体。
  • 少取模。
  • 减少 if 语句,尤其是在线段树中。

代码

#pragma GCC optimize("Ofast")
#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

typedef long long ll;

const int mod=998244353,maxn=2e5+5,inf=1e9;

int n,m,pos[maxn],buc[4],**w;
int dir[4][2]={{0,0},{-1,0},{0,-1},{-1,-1}};
int mn[maxn<<2],cnt[maxn<<2],la[maxn<<2];
ll s[maxn<<2];

#define getMemo(a) do { \
	a=new int*[n+2]; \
	for(int i=0;i<=n+1;++i) \
		a[i]=new int[m+2]; \
} while(0);

inline int inc(int x,int y) {
	return x+y>=mod?x+y-mod:x+y;
}

inline int dec(int x,int y) {
	return x-y<0?x-y+mod:x-y;
}

inline int Min(int x,int y) {
	return x<y?x:y;
}

void pushUp(int o) {
	int l=o<<1,r=o<<1|1;
	mn[o]=Min(mn[l],mn[r]);
	s[o]=cnt[o]=0;
	mn[o]==mn[l]?(
		s[o]=s[l],
		cnt[o]=cnt[l]
	):1;
	mn[o]==mn[r]?(
		s[o]=s[o]+s[r],
		cnt[o]=cnt[o]+cnt[r]
	):1;
}

void pushDown(int o) {
	int l=o<<1,r=o<<1|1; 
	la[o]?(
		la[l]=la[l]+la[o],
		la[r]=la[r]+la[o],
		mn[l]=mn[l]+la[o],
		mn[r]=mn[r]+la[o],
		la[o]=0
	):1;
}

void modify(int o,int l,int r,int L,int R,int k) {
	if(L>R) return;
	if(l>=L and r<=R) {
		la[o]=la[o]+k;
		mn[o]=mn[o]+k;
		return;
	}
	int mid=l+r>>1;
	pushDown(o);
	if(L<=mid) modify(o<<1,l,mid,L,R,k);
	if(mid<R) modify(o<<1|1,mid+1,r,L,R,k);
	pushUp(o);
}

void build(int o,int l,int r) {
	if(l==r) {
		cnt[o]=1,s[o]=l;
		mn[o]=inf;
		return;
	}
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushUp(o);
}

void Modify(int r,int c,int v) {
	for(int i=0;i<4;++i)
		buc[i]=w[r-dir[i][0]][c-dir[i][1]];
	for(int i=0;i<3;++i)
		for(int j=0;j<3;++j)
			buc[j]>buc[j+1]?(
				buc[j]^=buc[j+1]^=buc[j]^=buc[j+1]
			):1;
	if(buc[0]==v)
		modify(1,1,n*m,1,v,1);
	else if(buc[1]==v)
		modify(1,1,n*m,buc[0]+1,v,1),
		modify(1,1,n*m,1,buc[0],-1);
	else if(buc[2]==v)
		modify(1,1,n*m,buc[1]+1,v,1),
		modify(1,1,n*m,buc[0]+1,buc[1],-1),
		modify(1,1,n*m,1,buc[0],1);
	else if(buc[3]==v)
		modify(1,1,n*m,buc[2]+1,v,1),
		modify(1,1,n*m,buc[1]+1,buc[2],-1),
		modify(1,1,n*m,buc[0]+1,buc[1],1),
		modify(1,1,n*m,1,buc[0],-1);
}

int main() {
	n=read(9),m=read(9); getMemo(w);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			pos[w[i][j]=read(9)]=(i-1)*m+j;
	for(int i=0;i<=n+1;++i)
		w[i][0]=w[i][m+1]=n*m+1;
	for(int i=0;i<=m+1;++i)
		w[0][i]=w[n+1][i]=n*m+1;
	ll ans=0;
	build(1,1,n*m);
	for(int i=1;i<=n*m;++i) {
		int r=(pos[i]-1)/m+1,c=pos[i]-(r-1)*m;
		modify(1,1,n*m,i,i,-inf);
		for(int j=0;j<4;++j)
			Modify(r+dir[j][0],c+dir[j][1],i);
		if(mn[1]==4)
			ans=inc(ans,dec(1ll*(i+1)*cnt[1]%mod,s[1]%mod));
	}
	print(ans,'\n');
	return 0;
}
posted on 2021-09-12 15:53  Oxide  阅读(52)  评论(1编辑  收藏  举报