非确定性有穷状态决策自动机练习题Vol.2 C. 奇袭

非确定性有穷状态决策自动机练习题Vol.2 C. 奇袭

题目描述

由于各种原因,桐人现在被困在(Under World)(以下简称(UW))中,而(UW)马上 要迎来最终的压力测试——魔界入侵。

唯一一个神一般存在的(Administrator)被消灭了,靠原本的整合骑士的力量 是远远不够的。所以爱丽丝动员了(UW)全体人民,与整合骑士一起抗击魔族。

(UW)的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵前 发动一次奇袭,袭击魔族大本营! 为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族大本营进行侦查,并计算出袭击的难度。

经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个(N imes N)的网格图,一共有(N)支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。

在大本营中,每有一个(k×k(1≤k≤N))的子网格图包含恰好(k)支军队,我们袭 击的难度就会增加(1)点。

现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。

输入格式

第一行,一个正整数(N),表示网格图的大小以及军队数量。

接下来N行,每行两个整数,(Xi),(Yi),表示第(i)支军队的坐标。

保证每一行和每一列都恰有一只军队,即每一个(Xi)和每一个(Yi)都是不一样 的。

输出格式

一行,一个整数表示袭击的难度。

样例

样例输入

5
1 1
3 2
2 4
5 5
4 3

样例输出

10

样例解释

显然,分别以((2,2))((4,4))为左上,右下顶点的一个子网格图中有(3)支军队,

这为我们的难度贡献了(1)点。

类似的子网格图在原图中能找出(10)个。

数据范围与提示

对于(30\%)的数据,(N ≤ 100)

对于(60\%)的数据,(N ≤ 5000)

对于(100\%)的数据,(N ≤ 50000)

分析

用线段树即可解决

首先,我们发现,如果要满足 (k×k(1≤k≤N)) 的子网格图包含恰好 (k) 支军队

那么这 (k) 只军队的最大横坐标减去最小横坐标恰好等于这 (k) 只军队的最大纵坐标减最小纵坐标

两维不好处理,因此我们把横坐标作为下标,纵坐标作为权值

这样原问题就变成了在一个排列中有多少区间内的数是连续的

我们发现这可以用线段树去维护

我们把线段树的节点定义为以某个点为左端点,以扫到的点为右端点的区间中连续区间的个数

线段树要维护的信息有连续区间个数的最小值,该最小值的个数,以及区间加和操作中的 (lazy) 标记

每次从右边新加入一个点 (i) 时,我们把区间 ([1,i]) 整体加 (1)

代表此时又多了一个不连续的区间

此时我们去找 (a[i]+1)(a[i]-1) 的位置,如果它们的位置在 (i) 的左边,我们就把 ([1,a[i]-1]) 或者 ([1,a[i]+1]) 整体减一,代表包含 (a[i]+1) 或者 (a[i]-1) 的区间可以与 (a[i]) 合并形成一个大区间

每次枚举一个右端点时就单独计算一下价值

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
const int maxn=1e6+5;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int a[maxn],n,wz[maxn];
struct asd{
	int l,r,min,cnt,laz;
}tr[maxn<<1];
void push_up(int da){
	if(tr[da<<1].min==tr[da<<1|1].min){
		tr[da].min=tr[da<<1].min;
		tr[da].cnt=tr[da<<1].cnt+tr[da<<1|1].cnt;
	} else if(tr[da<<1].min<tr[da<<1|1].min){
		tr[da].min=tr[da<<1].min;
		tr[da].cnt=tr[da<<1].cnt;
	} else {
		tr[da].min=tr[da<<1|1].min;
		tr[da].cnt=tr[da<<1|1].cnt;
	}
}
void push_down(int da){
	if(tr[da].laz==0) return;
	tr[da<<1].min+=tr[da].laz;
	tr[da<<1|1].min+=tr[da].laz;
	tr[da<<1].laz+=tr[da].laz;
	tr[da<<1|1].laz+=tr[da].laz;
	tr[da].laz=0;
}
void build(int da,int l,int r){
	tr[da].l=l,tr[da].r=r;
	if(l==r){
		tr[da].min=1;
		tr[da].cnt=1;
		return;
	}
	int mids=(l+r)>>1;
	build(da<<1,l,mids);
	build(da<<1|1,mids+1,r);
	push_up(da);
}
int cx(int da,int l,int r){
	if(tr[da].l>=l && tr[da].r<=r){
		if(tr[da].min==1) return tr[da].cnt;
		return 0;
	}
	push_down(da);
	int mids=(tr[da].l+tr[da].r)>>1;
	int ans=0;
	if(l<=mids) ans+=cx(da<<1,l,r); 
	if(r>mids) ans+=cx(da<<1|1,l,r);
	return ans;
}
void xg(int da,int l,int r,int val){
	if(tr[da].l>=l && tr[da].r<=r){
		tr[da].min+=val;
		tr[da].laz+=val;
		return;
	}
	push_down(da);
	int mids=(tr[da].l+tr[da].r)>>1;
	if(l<=mids) xg(da<<1,l,r,val);
	if(r>mids) xg(da<<1|1,l,r,val);
	push_up(da);
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		int aa,bb;
		aa=read(),bb=read();
		a[aa]=bb;
		wz[a[aa]]=aa;
	}
	build(1,1,n);
	long long ans=0;	
	for(int i=1;i<=n;i++){
		if(i>1) xg(1,1,i-1,1);
		if(wz[a[i]-1]<=i && wz[a[i]-1]){
			xg(1,1,wz[a[i]-1],-1);
		}
		if(wz[a[i]+1]<=i && wz[a[i]+1]){
			xg(1,1,wz[a[i]+1],-1);
		}
		ans+=(long long)cx(1,1,i);
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/liuchanglc/p/13525720.html