?你为什么不庆祝 Oxide 卡大肠?

题目描述

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

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

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

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

解法

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


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

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


( ext{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(mlog m)) 的。

( ext{Step 2})(nle 2 imes 10^5)

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

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

题解给出了一种非常 ( m 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(nmlog 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,'
');
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/15258359.html