题目描述
(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;
}