zoukankan      html  css  js  c++  java
  • ?你为什么不庆祝 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;
    }
    
  • 相关阅读:
    道路计划猿练级(笔者:陈浩)
    OpenCV——Delaunay三角 [转载]
    mysql声明摘要
    JDBC公共动作类
    Android SharedPreferences复杂的存储
    SAP ABAP规划 SY-REPID与SY-CPROG差额
    在我的眼里,生产主管(1)——如何做竞争分析
    Oracle生成查询包括对应于所有数据表记录语句中指定的字段名
    SDUT 2894-C(最短spfa)
    人脉的本质
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15258359.html
Copyright © 2011-2022 走看看