LOJ6698 一键挖矿

一键挖矿

2098 年 4 月,Terraria 发布了 1.7 版本更新。

更新日志中显示,这个版本添加了一种新的机关,占用 \(n\times m\) 的矩形区域。将这个区域中第 \(i\) 行第 \(j\) 列的方块记作 \(\left<i,j\right>\)

每个方块有一个在 \(1\)\(nm\) 之间的权值,记作 \(w_{\left<i,j\right>}\),所有方块的权值互不相同。你可以选定两个参数 \(l,r\),满足 \(1\le l\le r\le nm\)。在此参数作用下,所有权值在 \([l,r]\) 外的方块将会虚化,只留下所有权值在 \([l,r]\) 内的方块。形式化地说,一个方块 \(\left<i,j\right>\) 会被保留当且仅当 \(l\le w_{\left<i,j\right>}\le r\)

你发现 1.7 版本仍兼容七十年前已停止更新的 tModLoader v1.23.7。你高兴地载入修改日期为 2020/9/21 19:44 的 VeinMiner.tmod 一键挖矿 Mod,想要试试它能不能对新的机关起作用。

一键挖矿 Mod 可以一次性采集所有与初始挖掘方块四连通未虚化的方块。也就是说,可以利用这个 Mod 采集所有的与初始挖掘方块在同一四连通块内的方块。

但是因为 Terraria 1.7 对方块更新进行了优化,所以这个 Mod 有一个 bug:如果所有与初始挖掘方块四连通的方块没有形成一个矩形区域,则无法完整地把这些方块全部采集下来。

你想知道有多少种选择参数 \(l,r\) 的方法,使得在参数作用下,能够使用一键挖矿 Mod 在不触发 bug 的情况下一次性采集所有未虚化的机关方块。

对于所有测试点:\(1\le w_{\left<i,j\right>}\le nm\le2\times10^5\)

题解

https://jklover.hs-blog.cf/2020/07/15/Loj-6698-一键挖矿/#more

线段树.

一维时是经典问题,但我们常用的单调栈 + 线段树做法并不能比较方便的搬到二维上,需要考虑另外一种条件转化.

加入权值在区间 \([l,r]\) 内的格子,令它们的颜色为黑色,其它的格子颜色为白色.

考虑所有的 \((n+1)\times (m+1)\)\(2\times 2\) 的小正方形(超出边界也算),则所有黑色格子形成一个矩形,当且仅当恰好有 \(4\) 个小正方形内部有 \(1\) 个黑色格子,并且没有任何一个小正方形内部有 \(3\) 个黑色格子.

必要性是显然的,任何一个由黑色格子组成的矩形都满足以上条件.充分性可以这样考虑,初始时一定是有 \(4\) 个黑色格子,要求恰好有 \(4\) 个小正方形内部有 \(1\) 个黑色格子,就必须用黑色格子将它们连起来,形成矩形的边界,而此时角的地方会出现包含 \(3\) 个黑色格子的小正方形,只有将内部全部填满后才会消失,于是可以得出这个条件是充分必要的.

有了这个结论,再来考虑如何计算答案.我们从小到大枚举 \(r\) ,并对每个 \(l\le r\) 维护 \(f(l)\) ,表示将权值在 \([l,r]\) 内的格子染黑后,有多少个小正方形内部有 \(1\) 个或 \(3\) 个黑色格子.不难发现 \(f(l)\ge 4,f(r)=4\) 是恒成立的,根据上面的结论,我们只需要求有多少个 \(f(l)=4\) ,即最小值的个数.

用线段树维护 \(f\) 以及最小值个数,每次 \(r\) 增加 \(1\) 时,会影响到周边的 \(4\)\(2\times 2\) 的小正方形,在线段树上区间加即可.

时间复杂度 \(O(nm\log nm)\) .

CO int N=2e5+10;
struct node {int min,cnt;};

IN node operator+(CO node&a,CO node&b){
	if(a.min!=b.min) return a.min<b.min?a:b;
	return {a.min,a.cnt+b.cnt};
}

node tree[4*N];
int tag[4*N];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
IN void put_tag(int x,int v){
	tree[x].min+=v,tag[x]+=v;
}
IN void push_down(int x){
	if(tag[x]){
		put_tag(lc,tag[x]),put_tag(rc,tag[x]);
		tag[x]=0;
	}
}
void build(int x,int l,int r){
	tree[x]={0,r-l+1};
	if(l==r) return;
	build(lc,l,mid),build(rc,mid+1,r);
}
void modify(int x,int l,int r,int ql,int qr,int v){
	if(ql<=l and r<=qr) return put_tag(x,v);
	push_down(x);
	if(ql<=mid) modify(lc,l,mid,ql,qr,v);
	if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
	tree[x]=tree[lc]+tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return tree[x];
	push_down(x);
	if(qr<=mid) return query(lc,l,mid,ql,qr);
	if(ql>mid) return query(rc,mid+1,r,ql,qr);
	return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}
#undef lc
#undef rc
#undef mid

int mx,px[N],py[N];
vector<int> w[N];

IN int f(int x){
	return x==1 or x==3;
}
void solve(vector<int> a){
	int x=a[0];
	a.push_back(0),sort(a.begin(),a.end());
	int p=lower_bound(a.begin(),a.end(),x)-a.begin();
	for(int i=1;i<=p;++i){
		int t=f(p-i+1)-f(p-i);
		modify(1,1,mx,a[i-1]+1,a[i],t);
	}
}
int main(){
	int n=read<int>(),m=read<int>();
	mx=n*m;
	for(int i=0;i<=n+1;++i){
		w[i].resize(m+2);
		w[i][0]=w[i][m+1]=mx+1;
		for(int j=1;j<=m;++j){
			if(i==0 or i==n+1) w[i][j]=mx+1;
			else read(w[i][j]),px[w[i][j]]=i,py[w[i][j]]=j;
		}
	}
	build(1,1,mx);
	int64 ans=0;
	for(int i=1;i<=mx;++i){
		int x=px[i],y=py[i];
		solve({w[x][y],w[x-1][y],w[x][y-1],w[x-1][y-1]});
		solve({w[x][y],w[x-1][y],w[x][y+1],w[x-1][y+1]});
		solve({w[x][y],w[x+1][y],w[x][y-1],w[x+1][y-1]});
		solve({w[x][y],w[x+1][y],w[x][y+1],w[x+1][y+1]});
		node res=query(1,1,mx,1,i);
		if(res.min==4) ans+=res.cnt;
	}
	printf("%lld\n",ans);
	return 0;
}

posted on 2020-07-17 22:07  autoint  阅读(135)  评论(0编辑  收藏

导航